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,
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.

View file

@ -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:

View file

@ -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;

View file

@ -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);

View file

@ -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() {

View file

@ -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
};

View file

@ -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;