don't restart domain-server if the only settings changes where permissions

This commit is contained in:
Seth Alves 2016-06-06 16:32:18 -07:00
parent d202a2bf11
commit 0c18df6278
7 changed files with 152 additions and 47 deletions

View file

@ -123,6 +123,61 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
} }
} }
void DomainGatekeeper::updateNodePermissions() {
// If the permissions were changed on the domain-server webpage (and nothing else was), a restart isn't required --
// we reprocess the permissions map and update the nodes here. The node list is frequently sent out to all
// the connected nodes, so these changes are propagated to other nodes.
QList<SharedNodePointer> nodesToKill;
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
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, SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeConnectionData& nodeConnection,
const PendingAssignedNodeData& pendingAssignment) { const PendingAssignedNodeData& pendingAssignment) {
@ -166,6 +221,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
// always allow assignment clients to create and destroy entities // always allow assignment clients to create and destroy entities
NodePermissions userPerms; NodePermissions userPerms;
userPerms.isAssignment = true;
userPerms.canAdjustLocks = true; userPerms.canAdjustLocks = true;
userPerms.canRezPermanentEntities = true; userPerms.canRezPermanentEntities = true;
newNode->setPermissions(userPerms); newNode->setPermissions(userPerms);
@ -184,7 +240,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
NodePermissions userPerms(username); NodePermissions userPerms(username);
userPerms.setAll(false); 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(); QHostAddress senderHostAddress = nodeConnection.senderSockAddr.getAddress();
bool isLocalUser = bool isLocalUser =
(senderHostAddress == limitedNodeList->getLocalSockAddr().getAddress() || senderHostAddress == QHostAddress::LocalHost); (senderHostAddress == limitedNodeList->getLocalSockAddr().getAddress() || senderHostAddress == QHostAddress::LocalHost);
@ -209,9 +265,10 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
qDebug() << "user-permissions: no username, so:" << userPerms; qDebug() << "user-permissions: no username, so:" << userPerms;
} else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { } else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) {
// they are sent us a username and the signature verifies it // they are sent us a username and the signature verifies it
userPerms.setUserName(username);
if (_server->_settingsManager.havePermissionsForName(username)) { if (_server->_settingsManager.havePermissionsForName(username)) {
// we have specific permissions for this user. // 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; qDebug() << "user-permissions: specific user matches, so:" << userPerms;
} else { } else {
// they are logged into metaverse, but we don't have specific permissions for them. // they are logged into metaverse, but we don't have specific permissions for them.

View file

@ -51,8 +51,12 @@ public slots:
void publicKeyJSONCallback(QNetworkReply& requestReply); void publicKeyJSONCallback(QNetworkReply& requestReply);
signals: signals:
void killNode(SharedNodePointer node);
void connectedNode(SharedNodePointer node); void connectedNode(SharedNodePointer node);
public slots:
void updateNodePermissions();
private slots: private slots:
void handlePeerPingTimeout(); void handlePeerPingTimeout();
private: private:

View file

@ -97,6 +97,13 @@ DomainServer::DomainServer(int argc, char* argv[]) :
// make sure we hear about newly connected nodes from our gatekeeper // make sure we hear about newly connected nodes from our gatekeeper
connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode); 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()) { if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth()) {
// we either read a certificate and private key or were not passed one // we either read a certificate and private key or were not passed one
// and completed login or did not need to // and completed login or did not need to
@ -2113,35 +2120,42 @@ void DomainServer::processPathQueryPacket(QSharedPointer<ReceivedMessage> messag
void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<ReceivedMessage> message) { void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<ReceivedMessage> message) {
// This packet has been matched to a source node and they're asking not to be in the domain anymore // This packet has been matched to a source node and they're asking not to be in the domain anymore
auto limitedNodeList = DependencyManager::get<LimitedNodeList>(); auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
const QUuid& nodeUUID = message->getSourceID(); const QUuid& nodeUUID = message->getSourceID();
qDebug() << "Received a disconnect request from node with UUID" << nodeUUID; 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 // 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 // packet to nodes that don't care about this type
auto nodeToKill = limitedNodeList->nodeWithUUID(nodeUUID); auto nodeToKill = limitedNodeList->nodeWithUUID(nodeUUID);
if (nodeToKill) { if (nodeToKill) {
auto nodeType = nodeToKill->getType(); handleKillNode(nodeToKill);
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<DomainServerNodeData*>(otherNode->getLinkedData());
return (nodeLinkedData != nullptr) && nodeLinkedData->getNodeInterestSet().contains(nodeType);
}, [&limitedNodeList](const SharedNodePointer& otherNode){
limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode);
});
} }
} }
void DomainServer::handleKillNode(SharedNodePointer nodeToKill) {
auto nodeType = nodeToKill->getType();
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
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<DomainServerNodeData*>(otherNode->getLinkedData());
return (nodeLinkedData != nullptr) && nodeLinkedData->getNodeInterestSet().contains(nodeType);
}, [&limitedNodeList](const SharedNodePointer& otherNode){
limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode);
});
}
void DomainServer::processICEServerHeartbeatDenialPacket(QSharedPointer<ReceivedMessage> message) { void DomainServer::processICEServerHeartbeatDenialPacket(QSharedPointer<ReceivedMessage> message) {
static const int NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN = 3; static const int NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN = 3;

View file

@ -111,6 +111,8 @@ private:
unsigned int countConnectedUsers(); unsigned int countConnectedUsers();
void handleKillNode(SharedNodePointer nodeToKill);
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr); void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr);
QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB); QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB);

View file

@ -92,7 +92,8 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<Re
} }
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) { void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
_configMap.loadMasterAndUserConfig(argumentList); _argumentList = argumentList;
_configMap.loadMasterAndUserConfig(_argumentList);
// What settings version were we before and what are we using now? // What settings version were we before and what are we using now?
// Do we need to do any re-mapping? // Do we need to do any re-mapping?
@ -136,7 +137,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
persistToFile(); persistToFile();
// reload the master and user config so that the merged config is right // reload the master and user config so that the merged config is right
_configMap.loadMasterAndUserConfig(argumentList); _configMap.loadMasterAndUserConfig(_argumentList);
} }
} }
@ -172,7 +173,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
persistToFile(); persistToFile();
// reload the master and user config so that the merged config is right // reload the master and user config so that the merged config is right
_configMap.loadMasterAndUserConfig(argumentList); _configMap.loadMasterAndUserConfig(_argumentList);
} }
} }
@ -195,7 +196,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
persistToFile(); persistToFile();
// reload the master and user config so the merged config is correct // reload the master and user config so the merged config is correct
_configMap.loadMasterAndUserConfig(argumentList); _configMap.loadMasterAndUserConfig(_argumentList);
} }
} }
@ -249,20 +250,19 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
} }
} }
packPermissions(argumentList); packPermissions();
_standardAgentPermissions.clear(); _standardAgentPermissions.clear();
_agentPermissions.clear(); _agentPermissions.clear();
} }
} }
unpackPermissions(argumentList); unpackPermissions();
// write the current description version to our settings // write the current description version to our settings
appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion); appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion);
} }
void DomainServerSettingsManager::packPermissionsForMap(const QStringList& argumentList, void DomainServerSettingsManager::packPermissionsForMap(QString mapName,
QString mapName,
QHash<QString, NodePermissionsPointer> agentPermissions, QHash<QString, NodePermissionsPointer> agentPermissions,
QString keyPath) { QString keyPath) {
QVariant* security = valueForKeyPath(_configMap.getUserConfig(), "security"); 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 // 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 // save settings for specific users
packPermissionsForMap(argumentList, "permissions", _agentPermissions, AGENT_PERMISSIONS_KEYPATH); packPermissionsForMap("permissions", _agentPermissions, AGENT_PERMISSIONS_KEYPATH);
persistToFile(); persistToFile();
_configMap.loadMasterAndUserConfig(argumentList); _configMap.loadMasterAndUserConfig(_argumentList);
} }
void DomainServerSettingsManager::unpackPermissions(const QStringList& argumentList) { void DomainServerSettingsManager::unpackPermissions() {
// transfer details from _configMap to _agentPermissions; // transfer details from _configMap to _agentPermissions;
_standardAgentPermissions.clear();
_agentPermissions.clear();
bool foundLocalhost = false; bool foundLocalhost = false;
bool foundAnonymous = false; bool foundAnonymous = false;
bool foundLoggedIn = false; bool foundLoggedIn = false;
@ -365,7 +368,7 @@ void DomainServerSettingsManager::unpackPermissions(const QStringList& argumentL
} }
if (needPack) { if (needPack) {
packPermissions(argumentList); packPermissions();
} }
#ifdef WANT_DEBUG #ifdef WANT_DEBUG
@ -463,7 +466,7 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection
qDebug() << "DomainServerSettingsManager postedObject -" << postedObject; qDebug() << "DomainServerSettingsManager postedObject -" << postedObject;
// we recurse one level deep below each group for the appropriate setting // 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 // store whatever the current _settingsMap is to file
persistToFile(); persistToFile();
@ -473,8 +476,13 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection
connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json"); connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json");
// defer a restart to the domain-server, this gives our HTTPConnection enough time to respond // defer a restart to the domain-server, this gives our HTTPConnection enough time to respond
const int DOMAIN_SERVER_RESTART_TIMER_MSECS = 1000; if (restartRequired) {
QTimer::singleShot(DOMAIN_SERVER_RESTART_TIMER_MSECS, qApp, SLOT(restart())); const int DOMAIN_SERVER_RESTART_TIMER_MSECS = 1000;
QTimer::singleShot(DOMAIN_SERVER_RESTART_TIMER_MSECS, qApp, SLOT(restart()));
} else {
unpackPermissions();
emit updateNodePermissions();
}
return true; return true;
} else if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH_JSON) { } else if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH_JSON) {
@ -677,9 +685,10 @@ QJsonObject DomainServerSettingsManager::settingDescriptionFromGroup(const QJson
return QJsonObject(); return QJsonObject();
} }
void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject) { bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject) {
auto& settingsVariant = _configMap.getUserConfig(); auto& settingsVariant = _configMap.getUserConfig();
bool needRestart = false;
// Iterate on the setting groups // Iterate on the setting groups
foreach(const QString& rootKey, postedObject.keys()) { foreach(const QString& rootKey, postedObject.keys()) {
QJsonValue rootValue = postedObject[rootKey]; QJsonValue rootValue = postedObject[rootKey];
@ -727,6 +736,9 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
if (!matchingDescriptionObject.isEmpty()) { if (!matchingDescriptionObject.isEmpty()) {
updateSetting(rootKey, rootValue, *thisMap, matchingDescriptionObject); updateSetting(rootKey, rootValue, *thisMap, matchingDescriptionObject);
if (rootKey != "security") {
needRestart = true;
}
} else { } else {
qDebug() << "Setting for root key" << rootKey << "does not exist - cannot update setting."; qDebug() << "Setting for root key" << rootKey << "does not exist - cannot update setting.";
} }
@ -740,6 +752,9 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
if (!matchingDescriptionObject.isEmpty()) { if (!matchingDescriptionObject.isEmpty()) {
QJsonValue settingValue = rootValue.toObject()[settingKey]; QJsonValue settingValue = rootValue.toObject()[settingKey];
updateSetting(settingKey, settingValue, *thisMap, matchingDescriptionObject); updateSetting(settingKey, settingValue, *thisMap, matchingDescriptionObject);
if (rootKey != "security") {
needRestart = true;
}
} else { } else {
qDebug() << "Could not find description for setting" << settingKey << "in group" << rootKey << qDebug() << "Could not find description for setting" << settingKey << "in group" << rootKey <<
"- cannot update setting."; "- cannot update setting.";
@ -755,6 +770,7 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
// re-merge the user and master configs after a settings change // re-merge the user and master configs after a settings change
_configMap.mergeMasterAndUserConfigs(); _configMap.mergeMasterAndUserConfigs();
return needRestart;
} }
void DomainServerSettingsManager::persistToFile() { void DomainServerSettingsManager::persistToFile() {

View file

@ -47,12 +47,18 @@ public:
NodePermissions getPermissionsForName(const QString& name) const; NodePermissions getPermissionsForName(const QString& name) const;
QStringList getAllNames() { return _agentPermissions.keys(); } QStringList getAllNames() { return _agentPermissions.keys(); }
signals:
void updateNodePermissions();
private slots: private slots:
void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message); void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message);
private: private:
QStringList _argumentList;
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false); 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, void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
const QJsonObject& settingDescription); const QJsonObject& settingDescription);
@ -65,10 +71,9 @@ private:
friend class DomainServer; friend class DomainServer;
void packPermissionsForMap(const QStringList& argumentList, QString mapName, void packPermissionsForMap(QString mapName, QHash<QString, NodePermissionsPointer> agentPermissions, QString keyPath);
QHash<QString, NodePermissionsPointer> agentPermissions, QString keyPath); void packPermissions();
void packPermissions(const QStringList& argumentList); void unpackPermissions();
void unpackPermissions(const QStringList& argumentList);
QHash<QString, NodePermissionsPointer> _standardAgentPermissions; // anonymous, logged-in, localhost QHash<QString, NodePermissionsPointer> _standardAgentPermissions; // anonymous, logged-in, localhost
QHash<QString, NodePermissionsPointer> _agentPermissions; // specific account-names QHash<QString, NodePermissionsPointer> _agentPermissions; // specific account-names
}; };

View file

@ -37,6 +37,12 @@ public:
QString getID() const { return _id; } 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. // these 3 names have special meaning.
static QString standardNameLocalhost; static QString standardNameLocalhost;
static QString standardNameLoggedIn; static QString standardNameLoggedIn;
@ -79,6 +85,7 @@ public:
protected: protected:
QString _id; QString _id;
QString _userName;
}; };
const NodePermissions DEFAULT_AGENT_PERMISSIONS; const NodePermissions DEFAULT_AGENT_PERMISSIONS;