mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 22:39:18 +02:00
collect permissions into their own data structure
This commit is contained in:
parent
e24ba4caf6
commit
83f2c723eb
15 changed files with 361 additions and 339 deletions
|
@ -286,8 +286,8 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<ReceivedMes
|
||||||
|
|
||||||
if (!senderID.isNull()) {
|
if (!senderID.isNull()) {
|
||||||
// We don't have this node yet - we should add it
|
// We don't have this node yet - we should add it
|
||||||
matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode
|
matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode(senderID, NodeType::Unassigned,
|
||||||
(senderID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false, false);
|
senderSockAddr, senderSockAddr);
|
||||||
|
|
||||||
auto childData = std::unique_ptr<AssignmentClientChildData>
|
auto childData = std::unique_ptr<AssignmentClientChildData>
|
||||||
{ new AssignmentClientChildData(Assignment::Type::AllTypes) };
|
{ new AssignmentClientChildData(Assignment::Type::AllTypes) };
|
||||||
|
|
|
@ -135,6 +135,12 @@
|
||||||
"label": "Write Assets",
|
"label": "Write Assets",
|
||||||
"type": "checkbox",
|
"type": "checkbox",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id_can_connect_past_max_capacity",
|
||||||
|
"label": "Ignore Max Capacity",
|
||||||
|
"type": "checkbox",
|
||||||
|
"default": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
//
|
|
||||||
// AgentPermissions.h
|
|
||||||
// domain-server/src
|
|
||||||
//
|
|
||||||
// Created by Seth Alves on 2016-6-1.
|
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_AgentPermissions_h
|
|
||||||
#define hifi_AgentPermissions_h
|
|
||||||
|
|
||||||
class AgentPermissions {
|
|
||||||
public:
|
|
||||||
AgentPermissions(QMap<QString, QVariant> perms) {
|
|
||||||
_id = perms["permissions_id"].toString();
|
|
||||||
canConnectToDomain = perms["id_can_connect"].toBool();
|
|
||||||
canAdjustLocks = perms["id_can_adjust_locks"].toBool();
|
|
||||||
canRezPermanentEntities = perms["id_can_rez"].toBool();
|
|
||||||
canRezTemporaryEntities = perms["id_can_rez_tmp"].toBool();
|
|
||||||
canWriteToAssetServer = perms["id_can_write_to_asset_server"].toBool();
|
|
||||||
};
|
|
||||||
|
|
||||||
QString getID() { return _id; }
|
|
||||||
|
|
||||||
bool canConnectToDomain { false };
|
|
||||||
bool canAdjustLocks { false };
|
|
||||||
bool canRezPermanentEntities { false };
|
|
||||||
bool canRezTemporaryEntities { false };
|
|
||||||
bool canWriteToAssetServer { false };
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QString _id;
|
|
||||||
};
|
|
||||||
|
|
||||||
using AgentPermissionsPointer = std::shared_ptr<AgentPermissions>;
|
|
||||||
|
|
||||||
#endif // hifi_AgentPermissions_h
|
|
|
@ -26,7 +26,7 @@ using SharedAssignmentPointer = QSharedPointer<Assignment>;
|
||||||
DomainGatekeeper::DomainGatekeeper(DomainServer* server) :
|
DomainGatekeeper::DomainGatekeeper(DomainServer* server) :
|
||||||
_server(server)
|
_server(server)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainGatekeeper::addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID,
|
void DomainGatekeeper::addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID,
|
||||||
|
@ -38,7 +38,7 @@ void DomainGatekeeper::addPendingAssignedNode(const QUuid& nodeUUID, const QUuid
|
||||||
|
|
||||||
QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID) {
|
QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID) {
|
||||||
auto it = _pendingAssignedNodes.find(tempUUID);
|
auto it = _pendingAssignedNodes.find(tempUUID);
|
||||||
|
|
||||||
if (it != _pendingAssignedNodes.end()) {
|
if (it != _pendingAssignedNodes.end()) {
|
||||||
return it->second.getAssignmentUUID();
|
return it->second.getAssignmentUUID();
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,57 +64,57 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
||||||
if (nodeConnection.protocolVersion != myProtocolVersion) {
|
if (nodeConnection.protocolVersion != myProtocolVersion) {
|
||||||
QString protocolVersionError = "Protocol version mismatch - Domain version:" + QCoreApplication::applicationVersion();
|
QString protocolVersionError = "Protocol version mismatch - Domain version:" + QCoreApplication::applicationVersion();
|
||||||
qDebug() << "Protocol Version mismatch - denying connection.";
|
qDebug() << "Protocol Version mismatch - denying connection.";
|
||||||
sendConnectionDeniedPacket(protocolVersionError, message->getSenderSockAddr(),
|
sendConnectionDeniedPacket(protocolVersionError, message->getSenderSockAddr(),
|
||||||
DomainHandler::ConnectionRefusedReason::ProtocolMismatch);
|
DomainHandler::ConnectionRefusedReason::ProtocolMismatch);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeConnection.localSockAddr.isNull() || nodeConnection.publicSockAddr.isNull()) {
|
if (nodeConnection.localSockAddr.isNull() || nodeConnection.publicSockAddr.isNull()) {
|
||||||
qDebug() << "Unexpected data received for node local socket or public socket. Will not allow connection.";
|
qDebug() << "Unexpected data received for node local socket or public socket. Will not allow connection.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const NodeSet VALID_NODE_TYPES {
|
static const NodeSet VALID_NODE_TYPES {
|
||||||
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent, NodeType::MessagesMixer
|
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent, NodeType::MessagesMixer
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) {
|
if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) {
|
||||||
qDebug() << "Received an invalid node type with connect request. Will not allow connection from"
|
qDebug() << "Received an invalid node type with connect request. Will not allow connection from"
|
||||||
<< nodeConnection.senderSockAddr << ": " << nodeConnection.nodeType;
|
<< nodeConnection.senderSockAddr << ": " << nodeConnection.nodeType;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if this connect request matches an assignment in the queue
|
// check if this connect request matches an assignment in the queue
|
||||||
auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID);
|
auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID);
|
||||||
|
|
||||||
SharedNodePointer node;
|
SharedNodePointer node;
|
||||||
|
|
||||||
if (pendingAssignment != _pendingAssignedNodes.end()) {
|
if (pendingAssignment != _pendingAssignedNodes.end()) {
|
||||||
node = processAssignmentConnectRequest(nodeConnection, pendingAssignment->second);
|
node = processAssignmentConnectRequest(nodeConnection, pendingAssignment->second);
|
||||||
} else if (!STATICALLY_ASSIGNED_NODES.contains(nodeConnection.nodeType)) {
|
} else if (!STATICALLY_ASSIGNED_NODES.contains(nodeConnection.nodeType)) {
|
||||||
QString username;
|
QString username;
|
||||||
QByteArray usernameSignature;
|
QByteArray usernameSignature;
|
||||||
|
|
||||||
if (message->getBytesLeftToRead() > 0) {
|
if (message->getBytesLeftToRead() > 0) {
|
||||||
// read username from packet
|
// read username from packet
|
||||||
packetStream >> username;
|
packetStream >> username;
|
||||||
|
|
||||||
if (message->getBytesLeftToRead() > 0) {
|
if (message->getBytesLeftToRead() > 0) {
|
||||||
// read user signature from packet
|
// read user signature from packet
|
||||||
packetStream >> usernameSignature;
|
packetStream >> usernameSignature;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node = processAgentConnectRequest(nodeConnection, username, usernameSignature);
|
node = processAgentConnectRequest(nodeConnection, username, usernameSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
// set the sending sock addr and node interest set on this node
|
// set the sending sock addr and node interest set on this node
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
nodeData->setSendingSockAddr(message->getSenderSockAddr());
|
nodeData->setSendingSockAddr(message->getSenderSockAddr());
|
||||||
nodeData->setNodeInterestSet(nodeConnection.interestList.toSet());
|
nodeData->setNodeInterestSet(nodeConnection.interestList.toSet());
|
||||||
nodeData->setPlaceName(nodeConnection.placeName);
|
nodeData->setPlaceName(nodeConnection.placeName);
|
||||||
|
|
||||||
// signal that we just connected a node so the DomainServer can get it a list
|
// signal that we just connected a node so the DomainServer can get it a list
|
||||||
// and broadcast its presence right away
|
// and broadcast its presence right away
|
||||||
emit connectedNode(node);
|
emit connectedNode(node);
|
||||||
|
@ -125,16 +125,16 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
||||||
|
|
||||||
SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeConnectionData& nodeConnection,
|
SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||||
const PendingAssignedNodeData& pendingAssignment) {
|
const PendingAssignedNodeData& pendingAssignment) {
|
||||||
|
|
||||||
// make sure this matches an assignment the DS told us we sent out
|
// make sure this matches an assignment the DS told us we sent out
|
||||||
auto it = _pendingAssignedNodes.find(nodeConnection.connectUUID);
|
auto it = _pendingAssignedNodes.find(nodeConnection.connectUUID);
|
||||||
|
|
||||||
SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer();
|
SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer();
|
||||||
|
|
||||||
if (it != _pendingAssignedNodes.end()) {
|
if (it != _pendingAssignedNodes.end()) {
|
||||||
// find the matching queued static assignment in DS queue
|
// find the matching queued static assignment in DS queue
|
||||||
matchingQueuedAssignment = _server->dequeueMatchingAssignment(it->second.getAssignmentUUID(), nodeConnection.nodeType);
|
matchingQueuedAssignment = _server->dequeueMatchingAssignment(it->second.getAssignmentUUID(), nodeConnection.nodeType);
|
||||||
|
|
||||||
if (matchingQueuedAssignment) {
|
if (matchingQueuedAssignment) {
|
||||||
qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID)
|
qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID)
|
||||||
<< "matches unfulfilled assignment"
|
<< "matches unfulfilled assignment"
|
||||||
|
@ -149,124 +149,85 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
|
||||||
qDebug() << "No assignment was deployed with UUID" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID);
|
qDebug() << "No assignment was deployed with UUID" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID);
|
||||||
return SharedNodePointer();
|
return SharedNodePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the new node
|
// add the new node
|
||||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
||||||
|
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||||
|
|
||||||
// set assignment related data on the linked data for this node
|
// set assignment related data on the linked data for this node
|
||||||
nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID());
|
nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID());
|
||||||
nodeData->setWalletUUID(it->second.getWalletUUID());
|
nodeData->setWalletUUID(it->second.getWalletUUID());
|
||||||
nodeData->setNodeVersion(it->second.getNodeVersion());
|
nodeData->setNodeVersion(it->second.getNodeVersion());
|
||||||
nodeData->setWasAssigned(true);
|
nodeData->setWasAssigned(true);
|
||||||
|
|
||||||
// cleanup the PendingAssignedNodeData for this assignment now that it's connecting
|
// cleanup the PendingAssignedNodeData for this assignment now that it's connecting
|
||||||
_pendingAssignedNodes.erase(it);
|
_pendingAssignedNodes.erase(it);
|
||||||
|
|
||||||
// always allow assignment clients to create and destroy entities
|
// always allow assignment clients to create and destroy entities
|
||||||
newNode->setIsAllowedEditor(true);
|
AgentPermissions userPerms;
|
||||||
newNode->setCanRez(true);
|
userPerms.canAdjustLocks = true;
|
||||||
|
userPerms.canRezPermanentEntities = true;
|
||||||
|
newNode->setPermissions(userPerms);
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
|
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
|
||||||
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
|
// const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
|
||||||
const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers";
|
// const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers";
|
||||||
|
|
||||||
SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||||
const QString& username,
|
const QString& username,
|
||||||
const QByteArray& usernameSignature) {
|
const QByteArray& usernameSignature) {
|
||||||
|
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
|
||||||
bool isRestrictingAccess =
|
// start with empty permissions
|
||||||
_server->_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
|
AgentPermissions 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 they are always allowed to connect
|
||||||
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);
|
||||||
|
if (isLocalUser) {
|
||||||
// if we're using restricted access and this user is not local make sure we got a user signature
|
userPerms |= _server->_settingsManager.getPermissionsForName("localhost");
|
||||||
if (isRestrictingAccess && !isLocalUser) {
|
|
||||||
if (!username.isEmpty()) {
|
|
||||||
if (usernameSignature.isEmpty()) {
|
|
||||||
// if user didn't include usernameSignature in connect request, send a connectionToken packet
|
|
||||||
sendConnectionTokenPacket(username, nodeConnection.senderSockAddr);
|
|
||||||
|
|
||||||
// ask for their public key right now to make sure we have it
|
|
||||||
requestUserPublicKey(username);
|
|
||||||
|
|
||||||
return SharedNodePointer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool verifiedUsername = false;
|
if (!username.isEmpty() && usernameSignature.isEmpty()) {
|
||||||
|
// user is attempting to prove their identity to us, but we don't have enough information
|
||||||
// if we do not have a local user we need to subject them to our verification and capacity checks
|
sendConnectionTokenPacket(username, nodeConnection.senderSockAddr);
|
||||||
if (!isLocalUser) {
|
// ask for their public key right now to make sure we have it
|
||||||
|
requestUserPublicKey(username);
|
||||||
// check if we need to look at the username signature
|
if (!isLocalUser) {
|
||||||
if (isRestrictingAccess) {
|
|
||||||
if (isVerifiedAllowedUser(username, usernameSignature, nodeConnection.senderSockAddr)) {
|
|
||||||
// we verified the user via their username and signature - set the verifiedUsername
|
|
||||||
// so we don't re-decrypt their sig if we're trying to exempt them from max capacity check (due to
|
|
||||||
// being in the allowed editors list)
|
|
||||||
verifiedUsername = true;
|
|
||||||
} else {
|
|
||||||
// failed to verify user - return a null shared ptr
|
|
||||||
return SharedNodePointer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isWithinMaxCapacity(username, usernameSignature, verifiedUsername, nodeConnection.senderSockAddr)) {
|
|
||||||
// we can't allow this user to connect because we are at max capacity (and they either aren't an allowed editor
|
|
||||||
// or couldn't be verified as one)
|
|
||||||
return SharedNodePointer();
|
return SharedNodePointer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this user is in the editors list (or if the editors list is empty) set the user's node's isAllowedEditor to true
|
if (username.isEmpty()) {
|
||||||
const QVariant* allowedEditorsVariant =
|
// they didn't tell us who they are
|
||||||
valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
|
userPerms |= _server->_settingsManager.getPermissionsForName("anonymous");
|
||||||
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
|
} else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) {
|
||||||
|
// they are sent us a username and the signature verifies it
|
||||||
// if the allowed editors list is empty then everyone can adjust locks
|
userPerms |= _server->_settingsManager.getPermissionsForName(username);
|
||||||
bool isAllowedEditor = allowedEditors.empty();
|
userPerms |= _server->_settingsManager.getPermissionsForName("logged-in");
|
||||||
|
} else {
|
||||||
if (allowedEditors.contains(username, Qt::CaseInsensitive)) {
|
// they sent us a username, but it didn't check out
|
||||||
// we have a non-empty allowed editors list - check if this user is verified to be in it
|
requestUserPublicKey(username);
|
||||||
if (!verifiedUsername) {
|
if (!isLocalUser) {
|
||||||
if (!verifyUserSignature(username, usernameSignature, HifiSockAddr())) {
|
return SharedNodePointer();
|
||||||
// failed to verify a user that is in the allowed editors list
|
|
||||||
|
|
||||||
// TODO: fix public key refresh in interface/metaverse and force this check
|
|
||||||
qDebug() << "Could not verify user" << username << "as allowed editor. In the interim this user"
|
|
||||||
<< "will be given edit rights to avoid a thrasing of public key requests and connect requests.";
|
|
||||||
}
|
|
||||||
|
|
||||||
isAllowedEditor = true;
|
|
||||||
} else {
|
|
||||||
// already verified this user and they are in the allowed editors list
|
|
||||||
isAllowedEditor = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if only editors should be able to rez entities
|
if (!userPerms.canConnectToDomain) {
|
||||||
const QVariant* editorsAreRezzersVariant =
|
return SharedNodePointer();
|
||||||
valueForKeyPath(_server->_settingsManager.getSettingsMap(), EDITORS_ARE_REZZERS_KEYPATH);
|
|
||||||
|
|
||||||
bool onlyEditorsAreRezzers = false;
|
|
||||||
if (editorsAreRezzersVariant) {
|
|
||||||
onlyEditorsAreRezzers = editorsAreRezzersVariant->toBool();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canRez = true;
|
if (!userPerms.canConnectPastMaxCapacity && !isWithinMaxCapacity()) {
|
||||||
if (onlyEditorsAreRezzers) {
|
// we can't allow this user to connect because we are at max capacity
|
||||||
canRez = isAllowedEditor;
|
sendConnectionDeniedPacket("Too many connected users.", nodeConnection.senderSockAddr,
|
||||||
|
DomainHandler::ConnectionRefusedReason::TooManyUsers);
|
||||||
|
return SharedNodePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
QUuid hintNodeID;
|
QUuid hintNodeID;
|
||||||
|
@ -285,24 +246,23 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
|
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
|
||||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID);
|
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID);
|
||||||
|
|
||||||
// set the edit rights for this user
|
// set the edit rights for this user
|
||||||
newNode->setIsAllowedEditor(isAllowedEditor);
|
newNode->setPermissions(userPerms);
|
||||||
newNode->setCanRez(canRez);
|
|
||||||
|
|
||||||
// grab the linked data for our new node so we can set the username
|
// grab the linked data for our new node so we can set the username
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||||
|
|
||||||
// if we have a username from the connect request, set it on the DomainServerNodeData
|
// if we have a username from the connect request, set it on the DomainServerNodeData
|
||||||
nodeData->setUsername(username);
|
nodeData->setUsername(username);
|
||||||
|
|
||||||
// also add an interpolation to DomainServerNodeData so that servers can get username in stats
|
// also add an interpolation to DomainServerNodeData so that servers can get username in stats
|
||||||
nodeData->addOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY,
|
nodeData->addOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY,
|
||||||
uuidStringWithoutCurlyBraces(newNode->getUUID()), username);
|
uuidStringWithoutCurlyBraces(newNode->getUUID()), username);
|
||||||
|
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,11 +270,11 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
|
||||||
QUuid nodeID) {
|
QUuid nodeID) {
|
||||||
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
|
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
|
||||||
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
|
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
|
||||||
|
|
||||||
if (connectedPeer) {
|
if (connectedPeer) {
|
||||||
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
||||||
nodeID = nodeConnection.connectUUID;
|
nodeID = nodeConnection.connectUUID;
|
||||||
|
|
||||||
if (connectedPeer->getActiveSocket()) {
|
if (connectedPeer->getActiveSocket()) {
|
||||||
// set their discovered socket to whatever the activated socket on the network peer object was
|
// set their discovered socket to whatever the activated socket on the network peer object was
|
||||||
discoveredSocket = *connectedPeer->getActiveSocket();
|
discoveredSocket = *connectedPeer->getActiveSocket();
|
||||||
|
@ -325,15 +285,15 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
|
||||||
nodeID = QUuid::createUuid();
|
nodeID = QUuid::createUuid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
|
||||||
SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeID, nodeConnection.nodeType,
|
SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeID, nodeConnection.nodeType,
|
||||||
nodeConnection.publicSockAddr, nodeConnection.localSockAddr);
|
nodeConnection.publicSockAddr, nodeConnection.localSockAddr);
|
||||||
|
|
||||||
// So that we can send messages to this node at will - we need to activate the correct socket on this node now
|
// So that we can send messages to this node at will - we need to activate the correct socket on this node now
|
||||||
newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket);
|
newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket);
|
||||||
|
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,21 +303,21 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
||||||
|
|
||||||
// it's possible this user can be allowed to connect, but we need to check their username signature
|
// it's possible this user can be allowed to connect, but we need to check their username signature
|
||||||
QByteArray publicKeyArray = _userPublicKeys.value(username);
|
QByteArray publicKeyArray = _userPublicKeys.value(username);
|
||||||
|
|
||||||
const QUuid& connectionToken = _connectionTokenHash.value(username.toLower());
|
const QUuid& connectionToken = _connectionTokenHash.value(username.toLower());
|
||||||
|
|
||||||
if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) {
|
if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) {
|
||||||
// if we do have a public key for the user, check for a signature match
|
// if we do have a public key for the user, check for a signature match
|
||||||
|
|
||||||
const unsigned char* publicKeyData = reinterpret_cast<const unsigned char*>(publicKeyArray.constData());
|
const unsigned char* publicKeyData = reinterpret_cast<const unsigned char*>(publicKeyArray.constData());
|
||||||
|
|
||||||
// first load up the public key into an RSA struct
|
// first load up the public key into an RSA struct
|
||||||
RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size());
|
RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size());
|
||||||
|
|
||||||
QByteArray lowercaseUsername = username.toLower().toUtf8();
|
QByteArray lowercaseUsername = username.toLower().toUtf8();
|
||||||
QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()),
|
QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()),
|
||||||
QCryptographicHash::Sha256);
|
QCryptographicHash::Sha256);
|
||||||
|
|
||||||
if (rsaPublicKey) {
|
if (rsaPublicKey) {
|
||||||
int decryptResult = RSA_verify(NID_sha256,
|
int decryptResult = RSA_verify(NID_sha256,
|
||||||
reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
|
reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
|
||||||
|
@ -365,29 +325,29 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
||||||
reinterpret_cast<const unsigned char*>(usernameSignature.constData()),
|
reinterpret_cast<const unsigned char*>(usernameSignature.constData()),
|
||||||
usernameSignature.size(),
|
usernameSignature.size(),
|
||||||
rsaPublicKey);
|
rsaPublicKey);
|
||||||
|
|
||||||
if (decryptResult == 1) {
|
if (decryptResult == 1) {
|
||||||
qDebug() << "Username signature matches for" << username << "- allowing connection.";
|
qDebug() << "Username signature matches for" << username << "- allowing connection.";
|
||||||
|
|
||||||
// free up the public key and remove connection token before we return
|
// free up the public key and remove connection token before we return
|
||||||
RSA_free(rsaPublicKey);
|
RSA_free(rsaPublicKey);
|
||||||
_connectionTokenHash.remove(username);
|
_connectionTokenHash.remove(username);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (!senderSockAddr.isNull()) {
|
if (!senderSockAddr.isNull()) {
|
||||||
qDebug() << "Error decrypting username signature for " << username << "- denying connection.";
|
qDebug() << "Error decrypting username signature for " << username << "- denying connection.";
|
||||||
sendConnectionDeniedPacket("Error decrypting username signature.", senderSockAddr,
|
sendConnectionDeniedPacket("Error decrypting username signature.", senderSockAddr,
|
||||||
DomainHandler::ConnectionRefusedReason::LoginError);
|
DomainHandler::ConnectionRefusedReason::LoginError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// free up the public key, we don't need it anymore
|
// free up the public key, we don't need it anymore
|
||||||
RSA_free(rsaPublicKey);
|
RSA_free(rsaPublicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// we can't let this user in since we couldn't convert their public key to an RSA key we could use
|
// we can't let this user in since we couldn't convert their public key to an RSA key we could use
|
||||||
if (!senderSockAddr.isNull()) {
|
if (!senderSockAddr.isNull()) {
|
||||||
qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection.";
|
qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection.";
|
||||||
|
@ -402,86 +362,35 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
||||||
DomainHandler::ConnectionRefusedReason::LoginError);
|
DomainHandler::ConnectionRefusedReason::LoginError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestUserPublicKey(username); // no joy. maybe next time?
|
requestUserPublicKey(username); // no joy. maybe next time?
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DomainGatekeeper::isVerifiedAllowedUser(const QString& username, const QByteArray& usernameSignature,
|
bool DomainGatekeeper::isWithinMaxCapacity() {
|
||||||
const HifiSockAddr& senderSockAddr) {
|
|
||||||
|
|
||||||
if (username.isEmpty()) {
|
|
||||||
qDebug() << "Connect request denied - no username provided.";
|
|
||||||
|
|
||||||
sendConnectionDeniedPacket("No username provided", senderSockAddr,
|
|
||||||
DomainHandler::ConnectionRefusedReason::LoginError);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList allowedUsers =
|
|
||||||
_server->_settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList();
|
|
||||||
|
|
||||||
if (allowedUsers.contains(username, Qt::CaseInsensitive)) {
|
|
||||||
if (!verifyUserSignature(username, usernameSignature, senderSockAddr)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "Connect request denied for user" << username << "- not in allowed users list.";
|
|
||||||
sendConnectionDeniedPacket("User not on whitelist.", senderSockAddr,
|
|
||||||
DomainHandler::ConnectionRefusedReason::NotAuthorized);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DomainGatekeeper::isWithinMaxCapacity(const QString& username, const QByteArray& usernameSignature,
|
|
||||||
bool& verifiedUsername,
|
|
||||||
const HifiSockAddr& senderSockAddr) {
|
|
||||||
// find out what our maximum capacity is
|
// find out what our maximum capacity is
|
||||||
const QVariant* maximumUserCapacityVariant = valueForKeyPath(_server->_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY);
|
const QVariant* maximumUserCapacityVariant =
|
||||||
|
valueForKeyPath(_server->_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY);
|
||||||
unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0;
|
unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0;
|
||||||
|
|
||||||
if (maximumUserCapacity > 0) {
|
if (maximumUserCapacity > 0) {
|
||||||
unsigned int connectedUsers = _server->countConnectedUsers();
|
unsigned int connectedUsers = _server->countConnectedUsers();
|
||||||
|
|
||||||
if (connectedUsers >= maximumUserCapacity) {
|
if (connectedUsers >= maximumUserCapacity) {
|
||||||
// too many users, deny the new connection unless this user is an allowed editor
|
|
||||||
|
|
||||||
const QVariant* allowedEditorsVariant =
|
|
||||||
valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
|
|
||||||
|
|
||||||
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
|
|
||||||
if (allowedEditors.contains(username)) {
|
|
||||||
if (verifiedUsername || verifyUserSignature(username, usernameSignature, senderSockAddr)) {
|
|
||||||
verifiedUsername = true;
|
|
||||||
qDebug() << "Above maximum capacity -" << connectedUsers << "/" << maximumUserCapacity <<
|
|
||||||
"but user" << username << "is in allowed editors list so will be allowed to connect.";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// deny connection from this user
|
|
||||||
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection.";
|
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection.";
|
||||||
sendConnectionDeniedPacket("Too many connected users.", senderSockAddr,
|
|
||||||
DomainHandler::ConnectionRefusedReason::TooManyUsers);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, allowing new connection.";
|
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, allowing new connection.";
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DomainGatekeeper::preloadAllowedUserPublicKeys() {
|
void DomainGatekeeper::preloadAllowedUserPublicKeys() {
|
||||||
const QVariant* allowedUsersVariant = valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH);
|
QStringList allowedUsers = _server->_settingsManager.getAllNames();
|
||||||
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
|
|
||||||
|
|
||||||
if (allowedUsers.size() > 0) {
|
if (allowedUsers.size() > 0) {
|
||||||
// in the future we may need to limit how many requests here - for now assume that lists of allowed users are not
|
// in the future we may need to limit how many requests here - for now assume that lists of allowed users are not
|
||||||
// going to create > 100 requests
|
// going to create > 100 requests
|
||||||
|
@ -496,11 +405,11 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) {
|
||||||
JSONCallbackParameters callbackParams;
|
JSONCallbackParameters callbackParams;
|
||||||
callbackParams.jsonCallbackReceiver = this;
|
callbackParams.jsonCallbackReceiver = this;
|
||||||
callbackParams.jsonCallbackMethod = "publicKeyJSONCallback";
|
callbackParams.jsonCallbackMethod = "publicKeyJSONCallback";
|
||||||
|
|
||||||
const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key";
|
const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key";
|
||||||
|
|
||||||
qDebug() << "Requesting public key for user" << username;
|
qDebug() << "Requesting public key for user" << username;
|
||||||
|
|
||||||
DependencyManager::get<AccountManager>()->sendRequest(USER_PUBLIC_KEY_PATH.arg(username),
|
DependencyManager::get<AccountManager>()->sendRequest(USER_PUBLIC_KEY_PATH.arg(username),
|
||||||
AccountManagerAuth::None,
|
AccountManagerAuth::None,
|
||||||
QNetworkAccessManager::GetOperation, callbackParams);
|
QNetworkAccessManager::GetOperation, callbackParams);
|
||||||
|
@ -508,38 +417,38 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) {
|
||||||
|
|
||||||
void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) {
|
void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) {
|
||||||
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
||||||
|
|
||||||
if (jsonObject["status"].toString() == "success") {
|
if (jsonObject["status"].toString() == "success") {
|
||||||
// figure out which user this is for
|
// figure out which user this is for
|
||||||
|
|
||||||
const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key";
|
const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key";
|
||||||
QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING);
|
QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING);
|
||||||
|
|
||||||
if (usernameRegex.indexIn(requestReply.url().toString()) != -1) {
|
if (usernameRegex.indexIn(requestReply.url().toString()) != -1) {
|
||||||
QString username = usernameRegex.cap(1);
|
QString username = usernameRegex.cap(1);
|
||||||
|
|
||||||
qDebug() << "Storing a public key for user" << username;
|
qDebug() << "Storing a public key for user" << username;
|
||||||
|
|
||||||
// pull the public key as a QByteArray from this response
|
// pull the public key as a QByteArray from this response
|
||||||
const QString JSON_DATA_KEY = "data";
|
const QString JSON_DATA_KEY = "data";
|
||||||
const QString JSON_PUBLIC_KEY_KEY = "public_key";
|
const QString JSON_PUBLIC_KEY_KEY = "public_key";
|
||||||
|
|
||||||
_userPublicKeys[username] =
|
_userPublicKeys[username] =
|
||||||
QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8());
|
QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainGatekeeper::sendConnectionDeniedPacket(const QString& reason, const HifiSockAddr& senderSockAddr,
|
void DomainGatekeeper::sendConnectionDeniedPacket(const QString& reason, const HifiSockAddr& senderSockAddr,
|
||||||
DomainHandler::ConnectionRefusedReason reasonCode) {
|
DomainHandler::ConnectionRefusedReason reasonCode) {
|
||||||
// this is an agent and we've decided we won't let them connect - send them a packet to deny connection
|
// this is an agent and we've decided we won't let them connect - send them a packet to deny connection
|
||||||
QByteArray utfString = reason.toUtf8();
|
QByteArray utfString = reason.toUtf8();
|
||||||
quint16 payloadSize = utfString.size();
|
quint16 payloadSize = utfString.size();
|
||||||
|
|
||||||
// setup the DomainConnectionDenied packet
|
// setup the DomainConnectionDenied packet
|
||||||
auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied,
|
auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied,
|
||||||
payloadSize + sizeof(payloadSize) + sizeof(uint8_t));
|
payloadSize + sizeof(payloadSize) + sizeof(uint8_t));
|
||||||
|
|
||||||
// pack in the reason the connection was denied (the client displays this)
|
// pack in the reason the connection was denied (the client displays this)
|
||||||
if (payloadSize > 0) {
|
if (payloadSize > 0) {
|
||||||
uint8_t reasonCodeWire = (uint8_t)reasonCode;
|
uint8_t reasonCodeWire = (uint8_t)reasonCode;
|
||||||
|
@ -547,7 +456,7 @@ void DomainGatekeeper::sendConnectionDeniedPacket(const QString& reason, const H
|
||||||
connectionDeniedPacket->writePrimitive(payloadSize);
|
connectionDeniedPacket->writePrimitive(payloadSize);
|
||||||
connectionDeniedPacket->write(utfString);
|
connectionDeniedPacket->write(utfString);
|
||||||
}
|
}
|
||||||
|
|
||||||
// send the packet off
|
// send the packet off
|
||||||
DependencyManager::get<LimitedNodeList>()->sendPacket(std::move(connectionDeniedPacket), senderSockAddr);
|
DependencyManager::get<LimitedNodeList>()->sendPacket(std::move(connectionDeniedPacket), senderSockAddr);
|
||||||
}
|
}
|
||||||
|
@ -555,20 +464,20 @@ void DomainGatekeeper::sendConnectionDeniedPacket(const QString& reason, const H
|
||||||
void DomainGatekeeper::sendConnectionTokenPacket(const QString& username, const HifiSockAddr& senderSockAddr) {
|
void DomainGatekeeper::sendConnectionTokenPacket(const QString& username, const HifiSockAddr& senderSockAddr) {
|
||||||
// get the existing connection token or create a new one
|
// get the existing connection token or create a new one
|
||||||
QUuid& connectionToken = _connectionTokenHash[username.toLower()];
|
QUuid& connectionToken = _connectionTokenHash[username.toLower()];
|
||||||
|
|
||||||
if (connectionToken.isNull()) {
|
if (connectionToken.isNull()) {
|
||||||
connectionToken = QUuid::createUuid();
|
connectionToken = QUuid::createUuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup a static connection token packet
|
// setup a static connection token packet
|
||||||
static auto connectionTokenPacket = NLPacket::create(PacketType::DomainServerConnectionToken, NUM_BYTES_RFC4122_UUID);
|
static auto connectionTokenPacket = NLPacket::create(PacketType::DomainServerConnectionToken, NUM_BYTES_RFC4122_UUID);
|
||||||
|
|
||||||
// reset the packet before each time we send
|
// reset the packet before each time we send
|
||||||
connectionTokenPacket->reset();
|
connectionTokenPacket->reset();
|
||||||
|
|
||||||
// write the connection token
|
// write the connection token
|
||||||
connectionTokenPacket->write(connectionToken.toRfc4122());
|
connectionTokenPacket->write(connectionToken.toRfc4122());
|
||||||
|
|
||||||
// send off the packet unreliably
|
// send off the packet unreliably
|
||||||
DependencyManager::get<LimitedNodeList>()->sendUnreliablePacket(*connectionTokenPacket, senderSockAddr);
|
DependencyManager::get<LimitedNodeList>()->sendUnreliablePacket(*connectionTokenPacket, senderSockAddr);
|
||||||
}
|
}
|
||||||
|
@ -576,33 +485,33 @@ void DomainGatekeeper::sendConnectionTokenPacket(const QString& username, const
|
||||||
const int NUM_PEER_PINGS_BEFORE_DELETE = 2000 / UDP_PUNCH_PING_INTERVAL_MS;
|
const int NUM_PEER_PINGS_BEFORE_DELETE = 2000 / UDP_PUNCH_PING_INTERVAL_MS;
|
||||||
|
|
||||||
void DomainGatekeeper::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) {
|
void DomainGatekeeper::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) {
|
||||||
|
|
||||||
if (peer->getConnectionAttempts() >= NUM_PEER_PINGS_BEFORE_DELETE) {
|
if (peer->getConnectionAttempts() >= NUM_PEER_PINGS_BEFORE_DELETE) {
|
||||||
// we've reached the maximum number of ping attempts
|
// we've reached the maximum number of ping attempts
|
||||||
qDebug() << "Maximum number of ping attempts reached for peer with ID" << peer->getUUID();
|
qDebug() << "Maximum number of ping attempts reached for peer with ID" << peer->getUUID();
|
||||||
qDebug() << "Removing from list of connecting peers.";
|
qDebug() << "Removing from list of connecting peers.";
|
||||||
|
|
||||||
_icePeers.remove(peer->getUUID());
|
_icePeers.remove(peer->getUUID());
|
||||||
} else {
|
} else {
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
|
||||||
// send the ping packet to the local and public sockets for this node
|
// send the ping packet to the local and public sockets for this node
|
||||||
auto localPingPacket = limitedNodeList->constructICEPingPacket(PingType::Local, limitedNodeList->getSessionUUID());
|
auto localPingPacket = limitedNodeList->constructICEPingPacket(PingType::Local, limitedNodeList->getSessionUUID());
|
||||||
limitedNodeList->sendPacket(std::move(localPingPacket), peer->getLocalSocket());
|
limitedNodeList->sendPacket(std::move(localPingPacket), peer->getLocalSocket());
|
||||||
|
|
||||||
auto publicPingPacket = limitedNodeList->constructICEPingPacket(PingType::Public, limitedNodeList->getSessionUUID());
|
auto publicPingPacket = limitedNodeList->constructICEPingPacket(PingType::Public, limitedNodeList->getSessionUUID());
|
||||||
limitedNodeList->sendPacket(std::move(publicPingPacket), peer->getPublicSocket());
|
limitedNodeList->sendPacket(std::move(publicPingPacket), peer->getPublicSocket());
|
||||||
|
|
||||||
peer->incrementConnectionAttempts();
|
peer->incrementConnectionAttempts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainGatekeeper::handlePeerPingTimeout() {
|
void DomainGatekeeper::handlePeerPingTimeout() {
|
||||||
NetworkPeer* senderPeer = qobject_cast<NetworkPeer*>(sender());
|
NetworkPeer* senderPeer = qobject_cast<NetworkPeer*>(sender());
|
||||||
|
|
||||||
if (senderPeer) {
|
if (senderPeer) {
|
||||||
SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID());
|
SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID());
|
||||||
|
|
||||||
if (sharedPeer && !sharedPeer->getActiveSocket()) {
|
if (sharedPeer && !sharedPeer->getActiveSocket()) {
|
||||||
pingPunchForConnectingPeer(sharedPeer);
|
pingPunchForConnectingPeer(sharedPeer);
|
||||||
}
|
}
|
||||||
|
@ -613,24 +522,24 @@ void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<ReceivedMe
|
||||||
// loop through the packet and pull out network peers
|
// loop through the packet and pull out network peers
|
||||||
// any peer we don't have we add to the hash, otherwise we update
|
// any peer we don't have we add to the hash, otherwise we update
|
||||||
QDataStream iceResponseStream(message->getMessage());
|
QDataStream iceResponseStream(message->getMessage());
|
||||||
|
|
||||||
NetworkPeer* receivedPeer = new NetworkPeer;
|
NetworkPeer* receivedPeer = new NetworkPeer;
|
||||||
iceResponseStream >> *receivedPeer;
|
iceResponseStream >> *receivedPeer;
|
||||||
|
|
||||||
if (!_icePeers.contains(receivedPeer->getUUID())) {
|
if (!_icePeers.contains(receivedPeer->getUUID())) {
|
||||||
qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer;
|
qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer;
|
||||||
SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer);
|
SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer);
|
||||||
_icePeers[receivedPeer->getUUID()] = newPeer;
|
_icePeers[receivedPeer->getUUID()] = newPeer;
|
||||||
|
|
||||||
// make sure we know when we should ping this peer
|
// make sure we know when we should ping this peer
|
||||||
connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainGatekeeper::handlePeerPingTimeout);
|
connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainGatekeeper::handlePeerPingTimeout);
|
||||||
|
|
||||||
// immediately ping the new peer, and start a timer to continue pinging it until we connect to it
|
// immediately ping the new peer, and start a timer to continue pinging it until we connect to it
|
||||||
newPeer->startPingTimer();
|
newPeer->startPingTimer();
|
||||||
|
|
||||||
qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID"
|
qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID"
|
||||||
<< newPeer->getUUID();
|
<< newPeer->getUUID();
|
||||||
|
|
||||||
pingPunchForConnectingPeer(newPeer);
|
pingPunchForConnectingPeer(newPeer);
|
||||||
} else {
|
} else {
|
||||||
delete receivedPeer;
|
delete receivedPeer;
|
||||||
|
@ -640,18 +549,18 @@ void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<ReceivedMe
|
||||||
void DomainGatekeeper::processICEPingPacket(QSharedPointer<ReceivedMessage> message) {
|
void DomainGatekeeper::processICEPingPacket(QSharedPointer<ReceivedMessage> message) {
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*message, limitedNodeList->getSessionUUID());
|
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*message, limitedNodeList->getSessionUUID());
|
||||||
|
|
||||||
limitedNodeList->sendPacket(std::move(pingReplyPacket), message->getSenderSockAddr());
|
limitedNodeList->sendPacket(std::move(pingReplyPacket), message->getSenderSockAddr());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<ReceivedMessage> message) {
|
void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<ReceivedMessage> message) {
|
||||||
QDataStream packetStream(message->getMessage());
|
QDataStream packetStream(message->getMessage());
|
||||||
|
|
||||||
QUuid nodeUUID;
|
QUuid nodeUUID;
|
||||||
packetStream >> nodeUUID;
|
packetStream >> nodeUUID;
|
||||||
|
|
||||||
SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID);
|
SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID);
|
||||||
|
|
||||||
if (sendingPeer) {
|
if (sendingPeer) {
|
||||||
// we had this NetworkPeer in our connecting list - add the right sock addr to our connected list
|
// we had this NetworkPeer in our connecting list - add the right sock addr to our connected list
|
||||||
sendingPeer->activateMatchingOrNewSymmetricSocket(message->getSenderSockAddr());
|
sendingPeer->activateMatchingOrNewSymmetricSocket(message->getSenderSockAddr());
|
||||||
|
|
|
@ -66,11 +66,7 @@ private:
|
||||||
|
|
||||||
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
|
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
|
||||||
const HifiSockAddr& senderSockAddr);
|
const HifiSockAddr& senderSockAddr);
|
||||||
bool isVerifiedAllowedUser(const QString& username, const QByteArray& usernameSignature,
|
bool isWithinMaxCapacity();
|
||||||
const HifiSockAddr& senderSockAddr);
|
|
||||||
bool isWithinMaxCapacity(const QString& username, const QByteArray& usernameSignature,
|
|
||||||
bool& verifiedUsername,
|
|
||||||
const HifiSockAddr& senderSockAddr);
|
|
||||||
|
|
||||||
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
|
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
|
||||||
const HifiSockAddr& senderSockAddr);
|
const HifiSockAddr& senderSockAddr);
|
||||||
|
|
|
@ -1084,8 +1084,7 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
|
||||||
// add a flag to indicate if this domain uses restricted access - for now that will exclude it from listings
|
// add a flag to indicate if this domain uses restricted access - for now that will exclude it from listings
|
||||||
const QString RESTRICTED_ACCESS_FLAG = "restricted";
|
const QString RESTRICTED_ACCESS_FLAG = "restricted";
|
||||||
|
|
||||||
domainObject[RESTRICTED_ACCESS_FLAG] =
|
domainObject[RESTRICTED_ACCESS_FLAG] = _settingsManager.getAllNames().length() > 0;
|
||||||
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
|
|
||||||
|
|
||||||
// figure out the breakdown of currently connected interface clients
|
// figure out the breakdown of currently connected interface clients
|
||||||
int numConnectedUnassigned = 0;
|
int numConnectedUnassigned = 0;
|
||||||
|
|
|
@ -110,6 +110,8 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
||||||
// This was prior to the introduction of security.restricted_access
|
// This was prior to the introduction of security.restricted_access
|
||||||
// If the user has a list of allowed users then set their value for security.restricted_access to true
|
// If the user has a list of allowed users then set their value for security.restricted_access to true
|
||||||
|
|
||||||
|
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
|
||||||
|
const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access";
|
||||||
QVariant* allowedUsers = valueForKeyPath(_configMap.getMergedConfig(), ALLOWED_USERS_SETTINGS_KEYPATH);
|
QVariant* allowedUsers = valueForKeyPath(_configMap.getMergedConfig(), ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||||
|
|
||||||
if (allowedUsers
|
if (allowedUsers
|
||||||
|
@ -207,10 +209,47 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServerSettingsManager::unpackPermissions() {
|
void DomainServerSettingsManager::unpackPermissions() {
|
||||||
QList<QVariant> permsHashList = valueOrDefaultValueForKeyPath(AGENT_PERMISSIONS_KEYPATH).toList();
|
bool foundLocalhost = false;
|
||||||
foreach (QVariant permsHash, permsHashList) {
|
bool foundAnonymous = false;
|
||||||
|
bool foundLoggedIn = false;
|
||||||
|
|
||||||
|
// XXX check for duplicate IDs
|
||||||
|
|
||||||
|
QVariant* permissions = valueForKeyPath(_configMap.getMergedConfig(), AGENT_PERMISSIONS_KEYPATH);
|
||||||
|
if (!permissions->canConvert(QMetaType::QVariantList)) {
|
||||||
|
qDebug() << "failed to extract permissions from settings.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// QList<QVariant> permissionsList = permissions->toList();
|
||||||
|
|
||||||
|
QVariantList* permissionsList = reinterpret_cast<QVariantList*>(permissions);
|
||||||
|
|
||||||
|
foreach (QVariant permsHash, *permissionsList) {
|
||||||
AgentPermissionsPointer perms { new AgentPermissions(permsHash.toMap()) };
|
AgentPermissionsPointer perms { new AgentPermissions(permsHash.toMap()) };
|
||||||
_agentPermissions[perms->getID()] = perms;
|
QString id = perms->getID();
|
||||||
|
foundLoggedIn |= (id == "localhost");
|
||||||
|
foundAnonymous |= (id == "anonymous");
|
||||||
|
foundLoggedIn |= (id == "logged-in");
|
||||||
|
_agentPermissions[id] = perms;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if any of the standard names are missing, add them
|
||||||
|
if (!foundLocalhost) {
|
||||||
|
AgentPermissionsPointer perms { new AgentPermissions("localhost") };
|
||||||
|
perms->setAll(true);
|
||||||
|
_agentPermissions["localhost"] = perms;
|
||||||
|
*permissionsList += perms->toVariant();
|
||||||
|
}
|
||||||
|
if (!foundAnonymous) {
|
||||||
|
AgentPermissionsPointer perms { new AgentPermissions("anonymous") };
|
||||||
|
_agentPermissions["anonymous"] = perms;
|
||||||
|
*permissionsList += perms->toVariant();
|
||||||
|
}
|
||||||
|
if (!foundLoggedIn) {
|
||||||
|
AgentPermissionsPointer perms { new AgentPermissions("logged-in") };
|
||||||
|
_agentPermissions["logged-in"] = perms;
|
||||||
|
*permissionsList += perms->toVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
|
@ -229,11 +268,13 @@ void DomainServerSettingsManager::unpackPermissions() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
AgentPermissionsPointer DomainServerSettingsManager::getPermissionsForName(QString name) const {
|
AgentPermissions DomainServerSettingsManager::getPermissionsForName(const QString& name) const {
|
||||||
if (_agentPermissions.contains(name)) {
|
if (_agentPermissions.contains(name)) {
|
||||||
return _agentPermissions[name];
|
return *(_agentPermissions[name].get());
|
||||||
}
|
}
|
||||||
return nullptr;
|
AgentPermissions nullPermissions;
|
||||||
|
nullPermissions.setAll(false);
|
||||||
|
return nullPermissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) {
|
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) {
|
||||||
|
|
|
@ -25,9 +25,6 @@ const QString SETTINGS_PATHS_KEY = "paths";
|
||||||
|
|
||||||
const QString SETTINGS_PATH = "/settings";
|
const QString SETTINGS_PATH = "/settings";
|
||||||
const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json";
|
const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json";
|
||||||
|
|
||||||
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
|
|
||||||
const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access";
|
|
||||||
const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions";
|
const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions";
|
||||||
|
|
||||||
class DomainServerSettingsManager : public QObject {
|
class DomainServerSettingsManager : public QObject {
|
||||||
|
@ -43,7 +40,8 @@ public:
|
||||||
QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); }
|
QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); }
|
||||||
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
|
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
|
||||||
|
|
||||||
AgentPermissionsPointer getPermissionsForName(QString name) const;
|
AgentPermissions getPermissionsForName(const QString& name) const;
|
||||||
|
QStringList getAllNames() { return _agentPermissions.keys(); }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message);
|
void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message);
|
||||||
|
|
43
libraries/networking/src/AgentPermissions.cpp
Normal file
43
libraries/networking/src/AgentPermissions.cpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
//
|
||||||
|
// AgentPermissions.cpp
|
||||||
|
// libraries/networking/src/
|
||||||
|
//
|
||||||
|
// Created by Seth Alves on 2016-6-1.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QDataStream>
|
||||||
|
#include "AgentPermissions.h"
|
||||||
|
|
||||||
|
AgentPermissions& AgentPermissions::operator|=(const AgentPermissions& rhs) {
|
||||||
|
this->canConnectToDomain |= rhs.canConnectToDomain;
|
||||||
|
this->canAdjustLocks |= rhs.canAdjustLocks;
|
||||||
|
this->canRezPermanentEntities |= rhs.canRezPermanentEntities;
|
||||||
|
this->canRezTemporaryEntities |= rhs.canRezTemporaryEntities;
|
||||||
|
this->canWriteToAssetServer |= rhs.canWriteToAssetServer;
|
||||||
|
this->canConnectPastMaxCapacity |= rhs.canConnectPastMaxCapacity;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& out, const AgentPermissions& perms) {
|
||||||
|
out << perms.canConnectToDomain;
|
||||||
|
out << perms.canAdjustLocks;
|
||||||
|
out << perms.canRezPermanentEntities;
|
||||||
|
out << perms.canRezTemporaryEntities;
|
||||||
|
out << perms.canWriteToAssetServer;
|
||||||
|
out << perms.canConnectPastMaxCapacity;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator>>(QDataStream& in, AgentPermissions& perms) {
|
||||||
|
in >> perms.canConnectToDomain;
|
||||||
|
in >> perms.canAdjustLocks;
|
||||||
|
in >> perms.canRezPermanentEntities;
|
||||||
|
in >> perms.canRezTemporaryEntities;
|
||||||
|
in >> perms.canWriteToAssetServer;
|
||||||
|
in >> perms.canConnectPastMaxCapacity;
|
||||||
|
return in;
|
||||||
|
}
|
79
libraries/networking/src/AgentPermissions.h
Normal file
79
libraries/networking/src/AgentPermissions.h
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
//
|
||||||
|
// AgentPermissions.h
|
||||||
|
// libraries/networking/src/
|
||||||
|
//
|
||||||
|
// Created by Seth Alves on 2016-6-1.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AgentPermissions_h
|
||||||
|
#define hifi_AgentPermissions_h
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
class AgentPermissions;
|
||||||
|
using AgentPermissionsPointer = std::shared_ptr<AgentPermissions>;
|
||||||
|
|
||||||
|
class AgentPermissions {
|
||||||
|
public:
|
||||||
|
AgentPermissions() { _id = QUuid::createUuid().toString(); }
|
||||||
|
AgentPermissions(const QString& name) { _id = name; }
|
||||||
|
AgentPermissions(QMap<QString, QVariant> perms) {
|
||||||
|
_id = perms["permissions_id"].toString();
|
||||||
|
canConnectToDomain = perms["id_can_connect"].toBool();
|
||||||
|
canAdjustLocks = perms["id_can_adjust_locks"].toBool();
|
||||||
|
canRezPermanentEntities = perms["id_can_rez"].toBool();
|
||||||
|
canRezTemporaryEntities = perms["id_can_rez_tmp"].toBool();
|
||||||
|
canWriteToAssetServer = perms["id_can_write_to_asset_server"].toBool();
|
||||||
|
canConnectPastMaxCapacity = perms["id_can_connect_past_max_capacity"].toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getID() { return _id; }
|
||||||
|
|
||||||
|
// the initializations here should match the defaults in describe-settings.json
|
||||||
|
bool canConnectToDomain { true };
|
||||||
|
bool canAdjustLocks { false };
|
||||||
|
bool canRezPermanentEntities { false };
|
||||||
|
bool canRezTemporaryEntities { false };
|
||||||
|
bool canWriteToAssetServer { false };
|
||||||
|
bool canConnectPastMaxCapacity { false };
|
||||||
|
|
||||||
|
void setAll(bool value) {
|
||||||
|
canConnectToDomain = value;
|
||||||
|
canAdjustLocks = value;
|
||||||
|
canRezPermanentEntities = value;
|
||||||
|
canRezTemporaryEntities = value;
|
||||||
|
canWriteToAssetServer = value;
|
||||||
|
canConnectPastMaxCapacity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant toVariant() {
|
||||||
|
QMap<QString, QVariant> values;
|
||||||
|
values["permissions_id"] = _id;
|
||||||
|
values["id_can_connect"] = canConnectToDomain;
|
||||||
|
values["id_can_adjust_locks"] = canAdjustLocks;
|
||||||
|
values["id_can_rez"] = canRezPermanentEntities;
|
||||||
|
values["id_can_rez_tmp"] = canRezTemporaryEntities;
|
||||||
|
values["id_can_write_to_asset_server"] = canWriteToAssetServer;
|
||||||
|
values["id_can_connect_past_max_capacity"] = canConnectPastMaxCapacity;
|
||||||
|
return QVariant(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentPermissions& operator|=(const AgentPermissions& rhs);
|
||||||
|
friend QDataStream& operator<<(QDataStream& out, const AgentPermissions& perms);
|
||||||
|
friend QDataStream& operator>>(QDataStream& in, AgentPermissions& perms);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString _id;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AgentPermissions DEFAULT_AGENT_PERMISSIONS;
|
||||||
|
|
||||||
|
#endif // hifi_AgentPermissions_h
|
|
@ -52,7 +52,7 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
|
||||||
_numCollectedPackets(0),
|
_numCollectedPackets(0),
|
||||||
_numCollectedBytes(0),
|
_numCollectedBytes(0),
|
||||||
_packetStatTimer(),
|
_packetStatTimer(),
|
||||||
_thisNodeCanRez(true)
|
_permissions(AgentPermissions())
|
||||||
{
|
{
|
||||||
static bool firstCall = true;
|
static bool firstCall = true;
|
||||||
if (firstCall) {
|
if (firstCall) {
|
||||||
|
@ -130,17 +130,25 @@ void LimitedNodeList::setSessionUUID(const QUuid& sessionUUID) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimitedNodeList::setIsAllowedEditor(bool isAllowedEditor) {
|
|
||||||
if (_isAllowedEditor != isAllowedEditor) {
|
|
||||||
_isAllowedEditor = isAllowedEditor;
|
|
||||||
emit isAllowedEditorChanged(isAllowedEditor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LimitedNodeList::setThisNodeCanRez(bool canRez) {
|
void LimitedNodeList::setPermissions(const AgentPermissions& newPermissions) {
|
||||||
if (_thisNodeCanRez != canRez) {
|
bool emitIsAllowedEditorChanged { false };
|
||||||
_thisNodeCanRez = canRez;
|
bool emitCanRezChanged { false };
|
||||||
emit canRezChanged(canRez);
|
|
||||||
|
if (_permissions.canAdjustLocks != newPermissions.canAdjustLocks) {
|
||||||
|
emitIsAllowedEditorChanged = true;
|
||||||
|
}
|
||||||
|
if (_permissions.canRezPermanentEntities != newPermissions.canRezPermanentEntities) {
|
||||||
|
emitCanRezChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_permissions = newPermissions;
|
||||||
|
|
||||||
|
if (emitIsAllowedEditorChanged) {
|
||||||
|
emit isAllowedEditorChanged(_permissions.canAdjustLocks);
|
||||||
|
}
|
||||||
|
if (emitCanRezChanged) {
|
||||||
|
emit canRezChanged(_permissions.canRezPermanentEntities);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,7 +523,7 @@ void LimitedNodeList::handleNodeKill(const SharedNodePointer& node) {
|
||||||
|
|
||||||
SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
||||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||||
bool isAllowedEditor, bool canRez,
|
const AgentPermissions& permissions,
|
||||||
const QUuid& connectionSecret) {
|
const QUuid& connectionSecret) {
|
||||||
NodeHash::const_iterator it = _nodeHash.find(uuid);
|
NodeHash::const_iterator it = _nodeHash.find(uuid);
|
||||||
|
|
||||||
|
@ -524,14 +532,13 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
||||||
|
|
||||||
matchingNode->setPublicSocket(publicSocket);
|
matchingNode->setPublicSocket(publicSocket);
|
||||||
matchingNode->setLocalSocket(localSocket);
|
matchingNode->setLocalSocket(localSocket);
|
||||||
matchingNode->setIsAllowedEditor(isAllowedEditor);
|
matchingNode->setPermissions(permissions);
|
||||||
matchingNode->setCanRez(canRez);
|
|
||||||
matchingNode->setConnectionSecret(connectionSecret);
|
matchingNode->setConnectionSecret(connectionSecret);
|
||||||
|
|
||||||
return matchingNode;
|
return matchingNode;
|
||||||
} else {
|
} else {
|
||||||
// we didn't have this node, so add them
|
// we didn't have this node, so add them
|
||||||
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, isAllowedEditor, canRez, connectionSecret, this);
|
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, permissions, connectionSecret, this);
|
||||||
|
|
||||||
if (nodeType == NodeType::AudioMixer) {
|
if (nodeType == NodeType::AudioMixer) {
|
||||||
LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer);
|
LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer);
|
||||||
|
|
|
@ -104,12 +104,10 @@ public:
|
||||||
const QUuid& getSessionUUID() const { return _sessionUUID; }
|
const QUuid& getSessionUUID() const { return _sessionUUID; }
|
||||||
void setSessionUUID(const QUuid& sessionUUID);
|
void setSessionUUID(const QUuid& sessionUUID);
|
||||||
|
|
||||||
bool isAllowedEditor() const { return _isAllowedEditor; }
|
void setPermissions(const AgentPermissions& newPermissions);
|
||||||
void setIsAllowedEditor(bool isAllowedEditor);
|
bool isAllowedEditor() const { return _permissions.canAdjustLocks; }
|
||||||
|
bool getThisNodeCanRez() const { return _permissions.canRezPermanentEntities; }
|
||||||
|
|
||||||
bool getThisNodeCanRez() const { return _thisNodeCanRez; }
|
|
||||||
void setThisNodeCanRez(bool canRez);
|
|
||||||
|
|
||||||
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
|
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
|
||||||
QUdpSocket& getDTLSSocket();
|
QUdpSocket& getDTLSSocket();
|
||||||
|
|
||||||
|
@ -137,7 +135,7 @@ public:
|
||||||
|
|
||||||
SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
||||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||||
bool isAllowedEditor = false, bool canRez = false,
|
const AgentPermissions& permissions = DEFAULT_AGENT_PERMISSIONS,
|
||||||
const QUuid& connectionSecret = QUuid());
|
const QUuid& connectionSecret = QUuid());
|
||||||
|
|
||||||
bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; }
|
bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; }
|
||||||
|
@ -300,8 +298,7 @@ protected:
|
||||||
int _numCollectedBytes;
|
int _numCollectedBytes;
|
||||||
|
|
||||||
QElapsedTimer _packetStatTimer;
|
QElapsedTimer _packetStatTimer;
|
||||||
bool _isAllowedEditor { false };
|
AgentPermissions _permissions;
|
||||||
bool _thisNodeCanRez;
|
|
||||||
|
|
||||||
QPointer<QTimer> _initialSTUNTimer;
|
QPointer<QTimer> _initialSTUNTimer;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "Node.h"
|
#include "Node.h"
|
||||||
#include "SharedUtil.h"
|
#include "SharedUtil.h"
|
||||||
|
#include "AgentPermissions.h"
|
||||||
|
|
||||||
#include <QtCore/QDataStream>
|
#include <QtCore/QDataStream>
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
|
@ -47,7 +48,7 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
|
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
|
||||||
const HifiSockAddr& localSocket, bool isAllowedEditor, bool canRez, const QUuid& connectionSecret,
|
const HifiSockAddr& localSocket, const AgentPermissions& permissions, const QUuid& connectionSecret,
|
||||||
QObject* parent) :
|
QObject* parent) :
|
||||||
NetworkPeer(uuid, publicSocket, localSocket, parent),
|
NetworkPeer(uuid, publicSocket, localSocket, parent),
|
||||||
_type(type),
|
_type(type),
|
||||||
|
@ -57,8 +58,7 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
|
||||||
_clockSkewUsec(0),
|
_clockSkewUsec(0),
|
||||||
_mutex(),
|
_mutex(),
|
||||||
_clockSkewMovingPercentile(30, 0.8f), // moving 80th percentile of 30 samples
|
_clockSkewMovingPercentile(30, 0.8f), // moving 80th percentile of 30 samples
|
||||||
_isAllowedEditor(isAllowedEditor),
|
_permissions(permissions)
|
||||||
_canRez(canRez)
|
|
||||||
{
|
{
|
||||||
// Update socket's object name
|
// Update socket's object name
|
||||||
setType(_type);
|
setType(_type);
|
||||||
|
@ -78,15 +78,12 @@ void Node::updateClockSkewUsec(qint64 clockSkewSample) {
|
||||||
_clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
|
_clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QDataStream& operator<<(QDataStream& out, const Node& node) {
|
QDataStream& operator<<(QDataStream& out, const Node& node) {
|
||||||
out << node._type;
|
out << node._type;
|
||||||
out << node._uuid;
|
out << node._uuid;
|
||||||
out << node._publicSocket;
|
out << node._publicSocket;
|
||||||
out << node._localSocket;
|
out << node._localSocket;
|
||||||
out << node._isAllowedEditor;
|
out << node._permissions;
|
||||||
out << node._canRez;
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,9 +92,7 @@ QDataStream& operator>>(QDataStream& in, Node& node) {
|
||||||
in >> node._uuid;
|
in >> node._uuid;
|
||||||
in >> node._publicSocket;
|
in >> node._publicSocket;
|
||||||
in >> node._localSocket;
|
in >> node._localSocket;
|
||||||
in >> node._isAllowedEditor;
|
in >> node._permissions;
|
||||||
in >> node._canRez;
|
|
||||||
|
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,13 +27,14 @@
|
||||||
#include "NodeType.h"
|
#include "NodeType.h"
|
||||||
#include "SimpleMovingAverage.h"
|
#include "SimpleMovingAverage.h"
|
||||||
#include "MovingPercentile.h"
|
#include "MovingPercentile.h"
|
||||||
|
#include "AgentPermissions.h"
|
||||||
|
|
||||||
class Node : public NetworkPeer {
|
class Node : public NetworkPeer {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Node(const QUuid& uuid, NodeType_t type,
|
Node(const QUuid& uuid, NodeType_t type,
|
||||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||||
bool isAllowedEditor, bool canRez, const QUuid& connectionSecret = QUuid(),
|
const AgentPermissions& permissions, const QUuid& connectionSecret = QUuid(),
|
||||||
QObject* parent = 0);
|
QObject* parent = 0);
|
||||||
|
|
||||||
bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; }
|
bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; }
|
||||||
|
@ -58,11 +59,10 @@ public:
|
||||||
void updateClockSkewUsec(qint64 clockSkewSample);
|
void updateClockSkewUsec(qint64 clockSkewSample);
|
||||||
QMutex& getMutex() { return _mutex; }
|
QMutex& getMutex() { return _mutex; }
|
||||||
|
|
||||||
void setIsAllowedEditor(bool isAllowedEditor) { _isAllowedEditor = isAllowedEditor; }
|
void setPermissions(const AgentPermissions& newPermissions) { _permissions = newPermissions; }
|
||||||
bool isAllowedEditor() { return _isAllowedEditor; }
|
AgentPermissions getPermissions() const { return _permissions; }
|
||||||
|
bool isAllowedEditor() const { return _permissions.canAdjustLocks; }
|
||||||
void setCanRez(bool canRez) { _canRez = canRez; }
|
bool getCanRez() const { return _permissions.canRezPermanentEntities; }
|
||||||
bool getCanRez() { return _canRez; }
|
|
||||||
|
|
||||||
friend QDataStream& operator<<(QDataStream& out, const Node& node);
|
friend QDataStream& operator<<(QDataStream& out, const Node& node);
|
||||||
friend QDataStream& operator>>(QDataStream& in, Node& node);
|
friend QDataStream& operator>>(QDataStream& in, Node& node);
|
||||||
|
@ -81,8 +81,7 @@ private:
|
||||||
qint64 _clockSkewUsec;
|
qint64 _clockSkewUsec;
|
||||||
QMutex _mutex;
|
QMutex _mutex;
|
||||||
MovingPercentile _clockSkewMovingPercentile;
|
MovingPercentile _clockSkewMovingPercentile;
|
||||||
bool _isAllowedEditor;
|
AgentPermissions _permissions;
|
||||||
bool _canRez;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Node*)
|
Q_DECLARE_METATYPE(Node*)
|
||||||
|
|
|
@ -527,7 +527,7 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
|
||||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList);
|
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList);
|
||||||
|
|
||||||
QDataStream packetStream(message->getMessage());
|
QDataStream packetStream(message->getMessage());
|
||||||
|
|
||||||
// grab the domain's ID from the beginning of the packet
|
// grab the domain's ID from the beginning of the packet
|
||||||
QUuid domainUUID;
|
QUuid domainUUID;
|
||||||
packetStream >> domainUUID;
|
packetStream >> domainUUID;
|
||||||
|
@ -543,14 +543,9 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
|
||||||
packetStream >> newUUID;
|
packetStream >> newUUID;
|
||||||
setSessionUUID(newUUID);
|
setSessionUUID(newUUID);
|
||||||
|
|
||||||
quint8 isAllowedEditor;
|
// pull the permissions/right/privileges for this node out of the stream
|
||||||
packetStream >> isAllowedEditor;
|
packetStream >> _permissions;
|
||||||
setIsAllowedEditor((bool) isAllowedEditor);
|
|
||||||
|
|
||||||
quint8 thisNodeCanRez;
|
|
||||||
packetStream >> thisNodeCanRez;
|
|
||||||
setThisNodeCanRez((bool) thisNodeCanRez);
|
|
||||||
|
|
||||||
// pull each node in the packet
|
// pull each node in the packet
|
||||||
while (packetStream.device()->pos() < message->getSize()) {
|
while (packetStream.device()->pos() < message->getSize()) {
|
||||||
parseNodeFromPacketStream(packetStream);
|
parseNodeFromPacketStream(packetStream);
|
||||||
|
@ -577,10 +572,9 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
||||||
qint8 nodeType;
|
qint8 nodeType;
|
||||||
QUuid nodeUUID, connectionUUID;
|
QUuid nodeUUID, connectionUUID;
|
||||||
HifiSockAddr nodePublicSocket, nodeLocalSocket;
|
HifiSockAddr nodePublicSocket, nodeLocalSocket;
|
||||||
bool isAllowedEditor;
|
AgentPermissions permissions;
|
||||||
bool canRez;
|
|
||||||
|
|
||||||
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> isAllowedEditor >> canRez;
|
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> permissions;
|
||||||
|
|
||||||
// if the public socket address is 0 then it's reachable at the same IP
|
// if the public socket address is 0 then it's reachable at the same IP
|
||||||
// as the domain server
|
// as the domain server
|
||||||
|
@ -591,8 +585,7 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
||||||
packetStream >> connectionUUID;
|
packetStream >> connectionUUID;
|
||||||
|
|
||||||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
|
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
|
||||||
nodeLocalSocket, isAllowedEditor, canRez,
|
nodeLocalSocket, permissions, connectionUUID);
|
||||||
connectionUUID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::sendAssignment(Assignment& assignment) {
|
void NodeList::sendAssignment(Assignment& assignment) {
|
||||||
|
|
Loading…
Reference in a new issue