mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 11:45:36 +02:00
don't restart domain-server if the only settings changes where permissions
This commit is contained in:
parent
d202a2bf11
commit
0c18df6278
7 changed files with 152 additions and 47 deletions
|
@ -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,
|
||||
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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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<ReceivedMessage> messag
|
|||
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
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
|
||||
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<DomainServerNodeData*>(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<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) {
|
||||
static const int NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN = 3;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -92,7 +92,8 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<Re
|
|||
}
|
||||
|
||||
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?
|
||||
// Do we need to do any re-mapping?
|
||||
|
@ -136,7 +137,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
persistToFile();
|
||||
|
||||
// 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();
|
||||
|
||||
// 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();
|
||||
|
||||
// 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();
|
||||
_agentPermissions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
unpackPermissions(argumentList);
|
||||
unpackPermissions();
|
||||
|
||||
// write the current description version to our settings
|
||||
appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion);
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::packPermissionsForMap(const QStringList& argumentList,
|
||||
QString mapName,
|
||||
void DomainServerSettingsManager::packPermissionsForMap(QString mapName,
|
||||
QHash<QString, NodePermissionsPointer> 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() {
|
||||
|
|
|
@ -47,12 +47,18 @@ public:
|
|||
NodePermissions getPermissionsForName(const QString& name) const;
|
||||
QStringList getAllNames() { return _agentPermissions.keys(); }
|
||||
|
||||
signals:
|
||||
void updateNodePermissions();
|
||||
|
||||
|
||||
private slots:
|
||||
void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> 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<QString, NodePermissionsPointer> agentPermissions, QString keyPath);
|
||||
void packPermissions(const QStringList& argumentList);
|
||||
void unpackPermissions(const QStringList& argumentList);
|
||||
void packPermissionsForMap(QString mapName, QHash<QString, NodePermissionsPointer> agentPermissions, QString keyPath);
|
||||
void packPermissions();
|
||||
void unpackPermissions();
|
||||
QHash<QString, NodePermissionsPointer> _standardAgentPermissions; // anonymous, logged-in, localhost
|
||||
QHash<QString, NodePermissionsPointer> _agentPermissions; // specific account-names
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue