From 0c18df62782aac62ef325ecebfbc7fc0b0aab95c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Jun 2016 16:32:18 -0700 Subject: [PATCH] don't restart domain-server if the only settings changes where permissions --- domain-server/src/DomainGatekeeper.cpp | 61 ++++++++++++++++++- domain-server/src/DomainGatekeeper.h | 6 +- domain-server/src/DomainServer.cpp | 54 ++++++++++------ domain-server/src/DomainServer.h | 2 + .../src/DomainServerSettingsManager.cpp | 54 ++++++++++------ .../src/DomainServerSettingsManager.h | 15 +++-- libraries/networking/src/NodePermissions.h | 7 +++ 7 files changed, 152 insertions(+), 47 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index adc95f3c38..0abae339c6 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -123,6 +123,61 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer nodesToKill; + + auto limitedNodeList = DependencyManager::get(); + limitedNodeList->eachNodeBreakable([this, limitedNodeList, &nodesToKill](const SharedNodePointer& node){ + QString username = node->getPermissions().getUserName(); + NodePermissions userPerms(username); + + if (node->getPermissions().isAssignment) { + // this node is an assignment-client + userPerms.isAssignment = true; + userPerms.canAdjustLocks = true; + userPerms.canRezPermanentEntities = true; + } else { + // this node is an agent + userPerms.setAll(false); + + const QHostAddress& addr = node->getLocalSocket().getAddress(); + bool isLocalUser = (addr == limitedNodeList->getLocalSockAddr().getAddress() || + addr == QHostAddress::LocalHost); + if (isLocalUser) { + userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLocalhost); + } + + if (username.isEmpty()) { + userPerms |= _server->_settingsManager.getPermissionsForName(NodePermissions::standardNameAnonymous); + } else { + if (_server->_settingsManager.havePermissionsForName(username)) { + userPerms = _server->_settingsManager.getPermissionsForName(username); + } else { + userPerms |= _server->_settingsManager.getPermissionsForName(NodePermissions::standardNameLoggedIn); + } + } + } + + node->setPermissions(userPerms); + + if (!userPerms.canConnectToDomain) { + qDebug() << "node" << node->getUUID() << "no longer has permission to connect."; + // hang up on this node + nodesToKill << node; + } + + return true; + }); + + foreach (auto node, nodesToKill) { + emit killNode(node); + } +} + SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeConnectionData& nodeConnection, const PendingAssignedNodeData& pendingAssignment) { @@ -166,6 +221,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo // always allow assignment clients to create and destroy entities NodePermissions userPerms; + userPerms.isAssignment = true; userPerms.canAdjustLocks = true; userPerms.canRezPermanentEntities = true; newNode->setPermissions(userPerms); @@ -184,7 +240,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect NodePermissions userPerms(username); userPerms.setAll(false); - // check if this user is on our local machine - if this is true they are always allowed to connect + // check if this user is on our local machine - if this is true set permissions to those for a "localhost" connection QHostAddress senderHostAddress = nodeConnection.senderSockAddr.getAddress(); bool isLocalUser = (senderHostAddress == limitedNodeList->getLocalSockAddr().getAddress() || senderHostAddress == QHostAddress::LocalHost); @@ -209,9 +265,10 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect qDebug() << "user-permissions: no username, so:" << userPerms; } else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { // they are sent us a username and the signature verifies it + userPerms.setUserName(username); if (_server->_settingsManager.havePermissionsForName(username)) { // we have specific permissions for this user. - userPerms |= _server->_settingsManager.getPermissionsForName(username); + userPerms = _server->_settingsManager.getPermissionsForName(username); qDebug() << "user-permissions: specific user matches, so:" << userPerms; } else { // they are logged into metaverse, but we don't have specific permissions for them. diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 352f84385e..bac50af2ec 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -51,8 +51,12 @@ public slots: void publicKeyJSONCallback(QNetworkReply& requestReply); signals: + void killNode(SharedNodePointer node); void connectedNode(SharedNodePointer node); - + +public slots: + void updateNodePermissions(); + private slots: void handlePeerPingTimeout(); private: diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 020983eb03..9abc605912 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -97,6 +97,13 @@ DomainServer::DomainServer(int argc, char* argv[]) : // make sure we hear about newly connected nodes from our gatekeeper connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode); + // if a connected node loses connection privileges, hang up on it + connect(&_gatekeeper, &DomainGatekeeper::killNode, this, &DomainServer::handleKillNode); + + // if permissions are updated, relay the changes to the Node datastructures + connect(&_settingsManager, &DomainServerSettingsManager::updateNodePermissions, + &_gatekeeper, &DomainGatekeeper::updateNodePermissions); + if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth()) { // we either read a certificate and private key or were not passed one // and completed login or did not need to @@ -2113,35 +2120,42 @@ void DomainServer::processPathQueryPacket(QSharedPointer messag void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer message) { // This packet has been matched to a source node and they're asking not to be in the domain anymore auto limitedNodeList = DependencyManager::get(); - + const QUuid& nodeUUID = message->getSourceID(); - + qDebug() << "Received a disconnect request from node with UUID" << nodeUUID; - + // we want to check what type this node was before going to kill it so that we can avoid sending the RemovedNode // packet to nodes that don't care about this type auto nodeToKill = limitedNodeList->nodeWithUUID(nodeUUID); - + if (nodeToKill) { - auto nodeType = nodeToKill->getType(); - limitedNodeList->killNodeWithUUID(nodeUUID); - - static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID); - - removedNodePacket->reset(); - removedNodePacket->write(nodeUUID.toRfc4122()); - - // broadcast out the DomainServerRemovedNode message - limitedNodeList->eachMatchingNode([&nodeType](const SharedNodePointer& otherNode) -> bool { - // only send the removed node packet to nodes that care about the type of node this was - auto nodeLinkedData = dynamic_cast(otherNode->getLinkedData()); - return (nodeLinkedData != nullptr) && nodeLinkedData->getNodeInterestSet().contains(nodeType); - }, [&limitedNodeList](const SharedNodePointer& otherNode){ - limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode); - }); + handleKillNode(nodeToKill); } } +void DomainServer::handleKillNode(SharedNodePointer nodeToKill) { + auto nodeType = nodeToKill->getType(); + auto limitedNodeList = DependencyManager::get(); + const QUuid& nodeUUID = nodeToKill->getUUID(); + + limitedNodeList->killNodeWithUUID(nodeUUID); + + static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID); + + removedNodePacket->reset(); + removedNodePacket->write(nodeUUID.toRfc4122()); + + // broadcast out the DomainServerRemovedNode message + limitedNodeList->eachMatchingNode([&nodeType](const SharedNodePointer& otherNode) -> bool { + // only send the removed node packet to nodes that care about the type of node this was + auto nodeLinkedData = dynamic_cast(otherNode->getLinkedData()); + return (nodeLinkedData != nullptr) && nodeLinkedData->getNodeInterestSet().contains(nodeType); + }, [&limitedNodeList](const SharedNodePointer& otherNode){ + limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode); + }); +} + void DomainServer::processICEServerHeartbeatDenialPacket(QSharedPointer message) { static const int NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN = 3; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index c39e405380..fb8a7d4e1a 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -111,6 +111,8 @@ private: unsigned int countConnectedUsers(); + void handleKillNode(SharedNodePointer nodeToKill); + void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr); QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB); diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index d52d926816..68e3ddcc2b 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -92,7 +92,8 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer agentPermissions, QString keyPath) { QVariant* security = valueForKeyPath(_configMap.getUserConfig(), "security"); @@ -285,20 +285,23 @@ void DomainServerSettingsManager::packPermissionsForMap(const QStringList& argum } } -void DomainServerSettingsManager::packPermissions(const QStringList& argumentList) { +void DomainServerSettingsManager::packPermissions() { // transfer details from _agentPermissions to _configMap - packPermissionsForMap(argumentList, "standard_permissions", _standardAgentPermissions, AGENT_STANDARD_PERMISSIONS_KEYPATH); + packPermissionsForMap("standard_permissions", _standardAgentPermissions, AGENT_STANDARD_PERMISSIONS_KEYPATH); // save settings for specific users - packPermissionsForMap(argumentList, "permissions", _agentPermissions, AGENT_PERMISSIONS_KEYPATH); + packPermissionsForMap("permissions", _agentPermissions, AGENT_PERMISSIONS_KEYPATH); persistToFile(); - _configMap.loadMasterAndUserConfig(argumentList); + _configMap.loadMasterAndUserConfig(_argumentList); } -void DomainServerSettingsManager::unpackPermissions(const QStringList& argumentList) { +void DomainServerSettingsManager::unpackPermissions() { // transfer details from _configMap to _agentPermissions; + _standardAgentPermissions.clear(); + _agentPermissions.clear(); + bool foundLocalhost = false; bool foundAnonymous = false; bool foundLoggedIn = false; @@ -365,7 +368,7 @@ void DomainServerSettingsManager::unpackPermissions(const QStringList& argumentL } if (needPack) { - packPermissions(argumentList); + packPermissions(); } #ifdef WANT_DEBUG @@ -463,7 +466,7 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection qDebug() << "DomainServerSettingsManager postedObject -" << postedObject; // we recurse one level deep below each group for the appropriate setting - recurseJSONObjectAndOverwriteSettings(postedObject); + bool restartRequired = recurseJSONObjectAndOverwriteSettings(postedObject); // store whatever the current _settingsMap is to file persistToFile(); @@ -473,8 +476,13 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json"); // defer a restart to the domain-server, this gives our HTTPConnection enough time to respond - const int DOMAIN_SERVER_RESTART_TIMER_MSECS = 1000; - QTimer::singleShot(DOMAIN_SERVER_RESTART_TIMER_MSECS, qApp, SLOT(restart())); + if (restartRequired) { + const int DOMAIN_SERVER_RESTART_TIMER_MSECS = 1000; + QTimer::singleShot(DOMAIN_SERVER_RESTART_TIMER_MSECS, qApp, SLOT(restart())); + } else { + unpackPermissions(); + emit updateNodePermissions(); + } return true; } else if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH_JSON) { @@ -677,9 +685,10 @@ QJsonObject DomainServerSettingsManager::settingDescriptionFromGroup(const QJson return QJsonObject(); } -void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject) { +bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject) { auto& settingsVariant = _configMap.getUserConfig(); - + bool needRestart = false; + // Iterate on the setting groups foreach(const QString& rootKey, postedObject.keys()) { QJsonValue rootValue = postedObject[rootKey]; @@ -727,6 +736,9 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ if (!matchingDescriptionObject.isEmpty()) { updateSetting(rootKey, rootValue, *thisMap, matchingDescriptionObject); + if (rootKey != "security") { + needRestart = true; + } } else { qDebug() << "Setting for root key" << rootKey << "does not exist - cannot update setting."; } @@ -740,6 +752,9 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ if (!matchingDescriptionObject.isEmpty()) { QJsonValue settingValue = rootValue.toObject()[settingKey]; updateSetting(settingKey, settingValue, *thisMap, matchingDescriptionObject); + if (rootKey != "security") { + needRestart = true; + } } else { qDebug() << "Could not find description for setting" << settingKey << "in group" << rootKey << "- cannot update setting."; @@ -755,6 +770,7 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ // re-merge the user and master configs after a settings change _configMap.mergeMasterAndUserConfigs(); + return needRestart; } void DomainServerSettingsManager::persistToFile() { diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 51ee657961..f6a5ef6f89 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -47,12 +47,18 @@ public: NodePermissions getPermissionsForName(const QString& name) const; QStringList getAllNames() { return _agentPermissions.keys(); } +signals: + void updateNodePermissions(); + + private slots: void processSettingsRequestPacket(QSharedPointer message); private: + QStringList _argumentList; + QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false); - void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject); + bool recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject); void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap, const QJsonObject& settingDescription); @@ -65,10 +71,9 @@ private: friend class DomainServer; - void packPermissionsForMap(const QStringList& argumentList, QString mapName, - QHash agentPermissions, QString keyPath); - void packPermissions(const QStringList& argumentList); - void unpackPermissions(const QStringList& argumentList); + void packPermissionsForMap(QString mapName, QHash agentPermissions, QString keyPath); + void packPermissions(); + void unpackPermissions(); QHash _standardAgentPermissions; // anonymous, logged-in, localhost QHash _agentPermissions; // specific account-names }; diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index adc13ba375..c153878a7e 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -37,6 +37,12 @@ public: QString getID() const { return _id; } + // the _id member isn't authenticated and _username is. + void setUserName(QString userName) { _userName = userName; } + QString getUserName() { return _userName; } + + bool isAssignment { false }; + // these 3 names have special meaning. static QString standardNameLocalhost; static QString standardNameLoggedIn; @@ -79,6 +85,7 @@ public: protected: QString _id; + QString _userName; }; const NodePermissions DEFAULT_AGENT_PERMISSIONS;