From 83f2c723eb3749a7c939429fd9724142fcd0c993 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 2 Jun 2016 13:15:30 -0700 Subject: [PATCH] collect permissions into their own data structure --- .../src/AssignmentClientMonitor.cpp | 4 +- .../resources/describe-settings.json | 6 + domain-server/src/AgentPermissions.h | 40 -- domain-server/src/DomainGatekeeper.cpp | 361 +++++++----------- domain-server/src/DomainGatekeeper.h | 6 +- domain-server/src/DomainServer.cpp | 3 +- .../src/DomainServerSettingsManager.cpp | 53 ++- .../src/DomainServerSettingsManager.h | 6 +- libraries/networking/src/AgentPermissions.cpp | 43 +++ libraries/networking/src/AgentPermissions.h | 79 ++++ libraries/networking/src/LimitedNodeList.cpp | 37 +- libraries/networking/src/LimitedNodeList.h | 13 +- libraries/networking/src/Node.cpp | 15 +- libraries/networking/src/Node.h | 15 +- libraries/networking/src/NodeList.cpp | 19 +- 15 files changed, 361 insertions(+), 339 deletions(-) delete mode 100644 domain-server/src/AgentPermissions.h create mode 100644 libraries/networking/src/AgentPermissions.cpp create mode 100644 libraries/networking/src/AgentPermissions.h diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 322fe6e57e..8ba253d549 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -286,8 +286,8 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer()->addOrUpdateNode - (senderID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false, false); + matchingNode = DependencyManager::get()->addOrUpdateNode(senderID, NodeType::Unassigned, + senderSockAddr, senderSockAddr); auto childData = std::unique_ptr { new AssignmentClientChildData(Assignment::Type::AllTypes) }; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index a856666bc9..b82a3ae1b1 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -135,6 +135,12 @@ "label": "Write Assets", "type": "checkbox", "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "default": false } ] } diff --git a/domain-server/src/AgentPermissions.h b/domain-server/src/AgentPermissions.h deleted file mode 100644 index 50c90581c1..0000000000 --- a/domain-server/src/AgentPermissions.h +++ /dev/null @@ -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 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; - -#endif // hifi_AgentPermissions_h diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index b940d46849..c80969c8b4 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -26,7 +26,7 @@ using SharedAssignmentPointer = QSharedPointer; DomainGatekeeper::DomainGatekeeper(DomainServer* server) : _server(server) { - + } 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) { auto it = _pendingAssignedNodes.find(tempUUID); - + if (it != _pendingAssignedNodes.end()) { return it->second.getAssignmentUUID(); } else { @@ -64,57 +64,57 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointergetSenderSockAddr(), + sendConnectionDeniedPacket(protocolVersionError, message->getSenderSockAddr(), DomainHandler::ConnectionRefusedReason::ProtocolMismatch); return; } - + if (nodeConnection.localSockAddr.isNull() || nodeConnection.publicSockAddr.isNull()) { qDebug() << "Unexpected data received for node local socket or public socket. Will not allow connection."; return; } - + static const NodeSet VALID_NODE_TYPES { NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent, NodeType::MessagesMixer }; - + if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) { qDebug() << "Received an invalid node type with connect request. Will not allow connection from" << nodeConnection.senderSockAddr << ": " << nodeConnection.nodeType; return; } - + // check if this connect request matches an assignment in the queue auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID); - + SharedNodePointer node; - + if (pendingAssignment != _pendingAssignedNodes.end()) { node = processAssignmentConnectRequest(nodeConnection, pendingAssignment->second); } else if (!STATICALLY_ASSIGNED_NODES.contains(nodeConnection.nodeType)) { QString username; QByteArray usernameSignature; - + if (message->getBytesLeftToRead() > 0) { // read username from packet packetStream >> username; - + if (message->getBytesLeftToRead() > 0) { // read user signature from packet packetStream >> usernameSignature; } } - + node = processAgentConnectRequest(nodeConnection, username, usernameSignature); } - + if (node) { // set the sending sock addr and node interest set on this node DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); nodeData->setSendingSockAddr(message->getSenderSockAddr()); nodeData->setNodeInterestSet(nodeConnection.interestList.toSet()); nodeData->setPlaceName(nodeConnection.placeName); - + // signal that we just connected a node so the DomainServer can get it a list // and broadcast its presence right away emit connectedNode(node); @@ -125,16 +125,16 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointerdequeueMatchingAssignment(it->second.getAssignmentUUID(), nodeConnection.nodeType); - + if (matchingQueuedAssignment) { qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID) << "matches unfulfilled assignment" @@ -149,124 +149,85 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo qDebug() << "No assignment was deployed with UUID" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID); return SharedNodePointer(); } - + // add the new node SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection); - + DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); - + // set assignment related data on the linked data for this node nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID()); nodeData->setWalletUUID(it->second.getWalletUUID()); nodeData->setNodeVersion(it->second.getNodeVersion()); nodeData->setWasAssigned(true); - + // cleanup the PendingAssignedNodeData for this assignment now that it's connecting _pendingAssignedNodes.erase(it); - + // always allow assignment clients to create and destroy entities - newNode->setIsAllowedEditor(true); - newNode->setCanRez(true); - + AgentPermissions userPerms; + userPerms.canAdjustLocks = true; + userPerms.canRezPermanentEntities = true; + newNode->setPermissions(userPerms); return newNode; } const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity"; -const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors"; -const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers"; +// const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors"; +// const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers"; SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection, const QString& username, const QByteArray& usernameSignature) { - + auto limitedNodeList = DependencyManager::get(); - - bool isRestrictingAccess = - _server->_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); - + + // start with empty permissions + 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 QHostAddress senderHostAddress = nodeConnection.senderSockAddr.getAddress(); bool isLocalUser = (senderHostAddress == limitedNodeList->getLocalSockAddr().getAddress() || senderHostAddress == QHostAddress::LocalHost); - - // if we're using restricted access and this user is not local make sure we got a user signature - 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(); - } - } + if (isLocalUser) { + userPerms |= _server->_settingsManager.getPermissionsForName("localhost"); } - - bool verifiedUsername = false; - - // if we do not have a local user we need to subject them to our verification and capacity checks - if (!isLocalUser) { - - // check if we need to look at the username signature - 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) + + if (!username.isEmpty() && usernameSignature.isEmpty()) { + // user is attempting to prove their identity to us, but we don't have enough information + sendConnectionTokenPacket(username, nodeConnection.senderSockAddr); + // ask for their public key right now to make sure we have it + requestUserPublicKey(username); + if (!isLocalUser) { 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 - const QVariant* allowedEditorsVariant = - valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH); - QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList(); - - // if the allowed editors list is empty then everyone can adjust locks - bool isAllowedEditor = allowedEditors.empty(); - - if (allowedEditors.contains(username, Qt::CaseInsensitive)) { - // we have a non-empty allowed editors list - check if this user is verified to be in it - if (!verifiedUsername) { - if (!verifyUserSignature(username, usernameSignature, HifiSockAddr())) { - // 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; + + if (username.isEmpty()) { + // they didn't tell us who they are + userPerms |= _server->_settingsManager.getPermissionsForName("anonymous"); + } else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { + // they are sent us a username and the signature verifies it + userPerms |= _server->_settingsManager.getPermissionsForName(username); + userPerms |= _server->_settingsManager.getPermissionsForName("logged-in"); + } else { + // they sent us a username, but it didn't check out + requestUserPublicKey(username); + if (!isLocalUser) { + return SharedNodePointer(); } } - - // check if only editors should be able to rez entities - const QVariant* editorsAreRezzersVariant = - valueForKeyPath(_server->_settingsManager.getSettingsMap(), EDITORS_ARE_REZZERS_KEYPATH); - - bool onlyEditorsAreRezzers = false; - if (editorsAreRezzersVariant) { - onlyEditorsAreRezzers = editorsAreRezzersVariant->toBool(); + + if (!userPerms.canConnectToDomain) { + return SharedNodePointer(); } - - bool canRez = true; - if (onlyEditorsAreRezzers) { - canRez = isAllowedEditor; + + if (!userPerms.canConnectPastMaxCapacity && !isWithinMaxCapacity()) { + // we can't allow this user to connect because we are at max capacity + sendConnectionDeniedPacket("Too many connected users.", nodeConnection.senderSockAddr, + DomainHandler::ConnectionRefusedReason::TooManyUsers); + return SharedNodePointer(); } QUuid hintNodeID; @@ -285,24 +246,23 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect return true; }); - + // add the connecting node (or re-use the matched one from eachNodeBreakable above) SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID); - + // set the edit rights for this user - newNode->setIsAllowedEditor(isAllowedEditor); - newNode->setCanRez(canRez); + newNode->setPermissions(userPerms); // grab the linked data for our new node so we can set the username DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); - + // if we have a username from the connect request, set it on the DomainServerNodeData nodeData->setUsername(username); - + // also add an interpolation to DomainServerNodeData so that servers can get username in stats nodeData->addOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY, uuidStringWithoutCurlyBraces(newNode->getUUID()), username); - + return newNode; } @@ -310,11 +270,11 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node QUuid nodeID) { HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr; SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID); - + if (connectedPeer) { // this user negotiated a connection with us via ICE, so re-use their ICE client ID nodeID = nodeConnection.connectUUID; - + if (connectedPeer->getActiveSocket()) { // set their discovered socket to whatever the activated socket on the network peer object was discoveredSocket = *connectedPeer->getActiveSocket(); @@ -325,15 +285,15 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node nodeID = QUuid::createUuid(); } } - + auto limitedNodeList = DependencyManager::get(); - + SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeID, nodeConnection.nodeType, 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 newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket); - + 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 QByteArray publicKeyArray = _userPublicKeys.value(username); - + const QUuid& connectionToken = _connectionTokenHash.value(username.toLower()); - + if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) { // if we do have a public key for the user, check for a signature match - + const unsigned char* publicKeyData = reinterpret_cast(publicKeyArray.constData()); - + // first load up the public key into an RSA struct RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size()); - + QByteArray lowercaseUsername = username.toLower().toUtf8(); QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()), QCryptographicHash::Sha256); - + if (rsaPublicKey) { int decryptResult = RSA_verify(NID_sha256, reinterpret_cast(usernameWithToken.constData()), @@ -365,29 +325,29 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, reinterpret_cast(usernameSignature.constData()), usernameSignature.size(), rsaPublicKey); - + if (decryptResult == 1) { qDebug() << "Username signature matches for" << username << "- allowing connection."; - + // free up the public key and remove connection token before we return RSA_free(rsaPublicKey); _connectionTokenHash.remove(username); - + return true; - + } else { if (!senderSockAddr.isNull()) { qDebug() << "Error decrypting username signature for " << username << "- denying connection."; sendConnectionDeniedPacket("Error decrypting username signature.", senderSockAddr, DomainHandler::ConnectionRefusedReason::LoginError); } - + // free up the public key, we don't need it anymore RSA_free(rsaPublicKey); } - + } else { - + // 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()) { 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); } } - + requestUserPublicKey(username); // no joy. maybe next time? return false; } -bool DomainGatekeeper::isVerifiedAllowedUser(const QString& username, const QByteArray& usernameSignature, - 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) { +bool DomainGatekeeper::isWithinMaxCapacity() { // 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; if (maximumUserCapacity > 0) { unsigned int connectedUsers = _server->countConnectedUsers(); 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."; - sendConnectionDeniedPacket("Too many connected users.", senderSockAddr, - DomainHandler::ConnectionRefusedReason::TooManyUsers); - return false; } - + qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, allowing new connection."; } - + return true; } void DomainGatekeeper::preloadAllowedUserPublicKeys() { - const QVariant* allowedUsersVariant = valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH); - QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); - + QStringList allowedUsers = _server->_settingsManager.getAllNames(); + 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 // going to create > 100 requests @@ -496,11 +405,11 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) { JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; callbackParams.jsonCallbackMethod = "publicKeyJSONCallback"; - + const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; - + qDebug() << "Requesting public key for user" << username; - + DependencyManager::get()->sendRequest(USER_PUBLIC_KEY_PATH.arg(username), AccountManagerAuth::None, QNetworkAccessManager::GetOperation, callbackParams); @@ -508,38 +417,38 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) { void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) { QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); - + if (jsonObject["status"].toString() == "success") { // figure out which user this is for - + const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key"; QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING); - + if (usernameRegex.indexIn(requestReply.url().toString()) != -1) { QString username = usernameRegex.cap(1); - + qDebug() << "Storing a public key for user" << username; - + // pull the public key as a QByteArray from this response const QString JSON_DATA_KEY = "data"; const QString JSON_PUBLIC_KEY_KEY = "public_key"; - + _userPublicKeys[username] = 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) { // 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(); quint16 payloadSize = utfString.size(); - + // setup the DomainConnectionDenied packet - auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, + auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, payloadSize + sizeof(payloadSize) + sizeof(uint8_t)); - + // pack in the reason the connection was denied (the client displays this) if (payloadSize > 0) { uint8_t reasonCodeWire = (uint8_t)reasonCode; @@ -547,7 +456,7 @@ void DomainGatekeeper::sendConnectionDeniedPacket(const QString& reason, const H connectionDeniedPacket->writePrimitive(payloadSize); connectionDeniedPacket->write(utfString); } - + // send the packet off DependencyManager::get()->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) { // get the existing connection token or create a new one QUuid& connectionToken = _connectionTokenHash[username.toLower()]; - + if (connectionToken.isNull()) { connectionToken = QUuid::createUuid(); } - + // setup a static connection token packet static auto connectionTokenPacket = NLPacket::create(PacketType::DomainServerConnectionToken, NUM_BYTES_RFC4122_UUID); - + // reset the packet before each time we send connectionTokenPacket->reset(); - + // write the connection token connectionTokenPacket->write(connectionToken.toRfc4122()); - + // send off the packet unreliably DependencyManager::get()->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; void DomainGatekeeper::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) { - + if (peer->getConnectionAttempts() >= NUM_PEER_PINGS_BEFORE_DELETE) { // we've reached the maximum number of ping attempts qDebug() << "Maximum number of ping attempts reached for peer with ID" << peer->getUUID(); qDebug() << "Removing from list of connecting peers."; - + _icePeers.remove(peer->getUUID()); } else { auto limitedNodeList = DependencyManager::get(); - + // send the ping packet to the local and public sockets for this node auto localPingPacket = limitedNodeList->constructICEPingPacket(PingType::Local, limitedNodeList->getSessionUUID()); limitedNodeList->sendPacket(std::move(localPingPacket), peer->getLocalSocket()); - + auto publicPingPacket = limitedNodeList->constructICEPingPacket(PingType::Public, limitedNodeList->getSessionUUID()); limitedNodeList->sendPacket(std::move(publicPingPacket), peer->getPublicSocket()); - + peer->incrementConnectionAttempts(); } } void DomainGatekeeper::handlePeerPingTimeout() { NetworkPeer* senderPeer = qobject_cast(sender()); - + if (senderPeer) { SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID()); - + if (sharedPeer && !sharedPeer->getActiveSocket()) { pingPunchForConnectingPeer(sharedPeer); } @@ -613,24 +522,24 @@ void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointergetMessage()); - + NetworkPeer* receivedPeer = new NetworkPeer; iceResponseStream >> *receivedPeer; - + if (!_icePeers.contains(receivedPeer->getUUID())) { qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer; SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer); _icePeers[receivedPeer->getUUID()] = newPeer; - + // make sure we know when we should ping this peer 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 newPeer->startPingTimer(); - + qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID" << newPeer->getUUID(); - + pingPunchForConnectingPeer(newPeer); } else { delete receivedPeer; @@ -640,18 +549,18 @@ void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer message) { auto limitedNodeList = DependencyManager::get(); auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*message, limitedNodeList->getSessionUUID()); - + limitedNodeList->sendPacket(std::move(pingReplyPacket), message->getSenderSockAddr()); } void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer message) { QDataStream packetStream(message->getMessage()); - + QUuid nodeUUID; packetStream >> nodeUUID; - + SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID); - + if (sendingPeer) { // we had this NetworkPeer in our connecting list - add the right sock addr to our connected list sendingPeer->activateMatchingOrNewSymmetricSocket(message->getSenderSockAddr()); diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 09e3b04ed7..352f84385e 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -66,11 +66,7 @@ private: bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr); - bool isVerifiedAllowedUser(const QString& username, const QByteArray& usernameSignature, - const HifiSockAddr& senderSockAddr); - bool isWithinMaxCapacity(const QString& username, const QByteArray& usernameSignature, - bool& verifiedUsername, - const HifiSockAddr& senderSockAddr); + bool isWithinMaxCapacity(); bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f6fbb3f470..ea3d953c63 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -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 const QString RESTRICTED_ACCESS_FLAG = "restricted"; - domainObject[RESTRICTED_ACCESS_FLAG] = - _settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); + domainObject[RESTRICTED_ACCESS_FLAG] = _settingsManager.getAllNames().length() > 0; // figure out the breakdown of currently connected interface clients int numConnectedUnassigned = 0; diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 0d6214d702..df299734cf 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -110,6 +110,8 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList // 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 + 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); if (allowedUsers @@ -207,10 +209,47 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList } void DomainServerSettingsManager::unpackPermissions() { - QList permsHashList = valueOrDefaultValueForKeyPath(AGENT_PERMISSIONS_KEYPATH).toList(); - foreach (QVariant permsHash, permsHashList) { + bool foundLocalhost = false; + 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 permissionsList = permissions->toList(); + + QVariantList* permissionsList = reinterpret_cast(permissions); + + foreach (QVariant permsHash, *permissionsList) { 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 @@ -229,11 +268,13 @@ void DomainServerSettingsManager::unpackPermissions() { #endif } -AgentPermissionsPointer DomainServerSettingsManager::getPermissionsForName(QString name) const { +AgentPermissions DomainServerSettingsManager::getPermissionsForName(const QString& name) const { 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) { diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 4d305b2b5b..3012e06c4d 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -25,9 +25,6 @@ const QString SETTINGS_PATHS_KEY = "paths"; const QString SETTINGS_PATH = "/settings"; 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"; class DomainServerSettingsManager : public QObject { @@ -43,7 +40,8 @@ public: QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); } QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); } - AgentPermissionsPointer getPermissionsForName(QString name) const; + AgentPermissions getPermissionsForName(const QString& name) const; + QStringList getAllNames() { return _agentPermissions.keys(); } private slots: void processSettingsRequestPacket(QSharedPointer message); diff --git a/libraries/networking/src/AgentPermissions.cpp b/libraries/networking/src/AgentPermissions.cpp new file mode 100644 index 0000000000..0d3483975b --- /dev/null +++ b/libraries/networking/src/AgentPermissions.cpp @@ -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 +#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; +} diff --git a/libraries/networking/src/AgentPermissions.h b/libraries/networking/src/AgentPermissions.h new file mode 100644 index 0000000000..d6598b8cbf --- /dev/null +++ b/libraries/networking/src/AgentPermissions.h @@ -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 +#include +#include +#include +#include + +class AgentPermissions; +using AgentPermissionsPointer = std::shared_ptr; + +class AgentPermissions { +public: + AgentPermissions() { _id = QUuid::createUuid().toString(); } + AgentPermissions(const QString& name) { _id = name; } + AgentPermissions(QMap 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 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 diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 9efe51183e..274d022ab5 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -52,7 +52,7 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short _numCollectedPackets(0), _numCollectedBytes(0), _packetStatTimer(), - _thisNodeCanRez(true) + _permissions(AgentPermissions()) { static bool firstCall = true; 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) { - if (_thisNodeCanRez != canRez) { - _thisNodeCanRez = canRez; - emit canRezChanged(canRez); +void LimitedNodeList::setPermissions(const AgentPermissions& newPermissions) { + bool emitIsAllowedEditorChanged { false }; + bool emitCanRezChanged { false }; + + 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, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, - bool isAllowedEditor, bool canRez, + const AgentPermissions& permissions, const QUuid& connectionSecret) { NodeHash::const_iterator it = _nodeHash.find(uuid); @@ -524,14 +532,13 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t matchingNode->setPublicSocket(publicSocket); matchingNode->setLocalSocket(localSocket); - matchingNode->setIsAllowedEditor(isAllowedEditor); - matchingNode->setCanRez(canRez); + matchingNode->setPermissions(permissions); matchingNode->setConnectionSecret(connectionSecret); return matchingNode; } else { // 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) { LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 5a3c10e8c3..c3378a4cf8 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -104,12 +104,10 @@ public: const QUuid& getSessionUUID() const { return _sessionUUID; } void setSessionUUID(const QUuid& sessionUUID); - bool isAllowedEditor() const { return _isAllowedEditor; } - void setIsAllowedEditor(bool isAllowedEditor); + void setPermissions(const AgentPermissions& newPermissions); + 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(); } QUdpSocket& getDTLSSocket(); @@ -137,7 +135,7 @@ public: SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, - bool isAllowedEditor = false, bool canRez = false, + const AgentPermissions& permissions = DEFAULT_AGENT_PERMISSIONS, const QUuid& connectionSecret = QUuid()); bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; } @@ -300,8 +298,7 @@ protected: int _numCollectedBytes; QElapsedTimer _packetStatTimer; - bool _isAllowedEditor { false }; - bool _thisNodeCanRez; + AgentPermissions _permissions; QPointer _initialSTUNTimer; diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 1e1cec2413..94f32a4e01 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -16,6 +16,7 @@ #include "Node.h" #include "SharedUtil.h" +#include "AgentPermissions.h" #include #include @@ -47,7 +48,7 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) { } 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) : NetworkPeer(uuid, publicSocket, localSocket, parent), _type(type), @@ -57,8 +58,7 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, _clockSkewUsec(0), _mutex(), _clockSkewMovingPercentile(30, 0.8f), // moving 80th percentile of 30 samples - _isAllowedEditor(isAllowedEditor), - _canRez(canRez) + _permissions(permissions) { // Update socket's object name setType(_type); @@ -78,15 +78,12 @@ void Node::updateClockSkewUsec(qint64 clockSkewSample) { _clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile(); } - QDataStream& operator<<(QDataStream& out, const Node& node) { out << node._type; out << node._uuid; out << node._publicSocket; out << node._localSocket; - out << node._isAllowedEditor; - out << node._canRez; - + out << node._permissions; return out; } @@ -95,9 +92,7 @@ QDataStream& operator>>(QDataStream& in, Node& node) { in >> node._uuid; in >> node._publicSocket; in >> node._localSocket; - in >> node._isAllowedEditor; - in >> node._canRez; - + in >> node._permissions; return in; } diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 3927672319..abc3cfa181 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -27,13 +27,14 @@ #include "NodeType.h" #include "SimpleMovingAverage.h" #include "MovingPercentile.h" +#include "AgentPermissions.h" class Node : public NetworkPeer { Q_OBJECT public: Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, - bool isAllowedEditor, bool canRez, const QUuid& connectionSecret = QUuid(), + const AgentPermissions& permissions, const QUuid& connectionSecret = QUuid(), QObject* parent = 0); bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; } @@ -58,11 +59,10 @@ public: void updateClockSkewUsec(qint64 clockSkewSample); QMutex& getMutex() { return _mutex; } - void setIsAllowedEditor(bool isAllowedEditor) { _isAllowedEditor = isAllowedEditor; } - bool isAllowedEditor() { return _isAllowedEditor; } - - void setCanRez(bool canRez) { _canRez = canRez; } - bool getCanRez() { return _canRez; } + void setPermissions(const AgentPermissions& newPermissions) { _permissions = newPermissions; } + AgentPermissions getPermissions() const { return _permissions; } + bool isAllowedEditor() const { return _permissions.canAdjustLocks; } + bool getCanRez() const { return _permissions.canRezPermanentEntities; } friend QDataStream& operator<<(QDataStream& out, const Node& node); friend QDataStream& operator>>(QDataStream& in, Node& node); @@ -81,8 +81,7 @@ private: qint64 _clockSkewUsec; QMutex _mutex; MovingPercentile _clockSkewMovingPercentile; - bool _isAllowedEditor; - bool _canRez; + AgentPermissions _permissions; }; Q_DECLARE_METATYPE(Node*) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 16a4083b08..b9f6da8c00 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -527,7 +527,7 @@ void NodeList::processDomainServerList(QSharedPointer message) DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList); QDataStream packetStream(message->getMessage()); - + // grab the domain's ID from the beginning of the packet QUuid domainUUID; packetStream >> domainUUID; @@ -543,14 +543,9 @@ void NodeList::processDomainServerList(QSharedPointer message) packetStream >> newUUID; setSessionUUID(newUUID); - quint8 isAllowedEditor; - packetStream >> isAllowedEditor; - setIsAllowedEditor((bool) isAllowedEditor); + // pull the permissions/right/privileges for this node out of the stream + packetStream >> _permissions; - quint8 thisNodeCanRez; - packetStream >> thisNodeCanRez; - setThisNodeCanRez((bool) thisNodeCanRez); - // pull each node in the packet while (packetStream.device()->pos() < message->getSize()) { parseNodeFromPacketStream(packetStream); @@ -577,10 +572,9 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) { qint8 nodeType; QUuid nodeUUID, connectionUUID; HifiSockAddr nodePublicSocket, nodeLocalSocket; - bool isAllowedEditor; - bool canRez; + AgentPermissions permissions; - 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 // as the domain server @@ -591,8 +585,7 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) { packetStream >> connectionUUID; SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, - nodeLocalSocket, isAllowedEditor, canRez, - connectionUUID); + nodeLocalSocket, permissions, connectionUUID); } void NodeList::sendAssignment(Assignment& assignment) {