diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index 257dcaa783..dd0e4ad4a1 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -845,6 +845,78 @@
}
],
+ "columns": [
+ {
+ "name": "permissions_id",
+ "label": ""
+ },
+ {
+ "name": "id_can_connect",
+ "label": "Connect",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_adjust_locks",
+ "label": "Lock / Unlock",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_rez",
+ "label": "Rez",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_rez_tmp",
+ "label": "Rez Temporary",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_write_to_asset_server",
+ "label": "Write Assets",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_connect_past_max_capacity",
+ "label": "Ignore Max Capacity",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_kick",
+ "label": "Kick Users",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ }
+ ]
+ },
+ {
+ "name": "machine_fingerprint_permissions",
+ "type": "table",
+ "caption": "Permissions for Users with Machine Fingerprints",
+ "can_add_new_rows": true,
+ "groups": [
+ {
+ "label": "Machine Fingerprint",
+ "span": 1
+ },
+ {
+ "label": "Permissions ?",
+ "span": 7
+ }
+ ],
+
"columns": [
{
"name": "permissions_id",
diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp
index f55a2073d1..97f3e1a697 100644
--- a/domain-server/src/DomainGatekeeper.cpp
+++ b/domain-server/src/DomainGatekeeper.cpp
@@ -120,19 +120,21 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointersetPlaceName(nodeConnection.placeName);
qDebug() << "Allowed connection from node" << uuidStringWithoutCurlyBraces(node->getUUID())
- << "on" << message->getSenderSockAddr() << "with MAC" << nodeConnection.hardwareAddress;
+ << "on" << message->getSenderSockAddr() << "with MAC" << nodeConnection.hardwareAddress
+ << "and machine fingerprint" << nodeConnection.machineFingerprint;
// signal that we just connected a node so the DomainServer can get it a list
// and broadcast its presence right away
emit connectedNode(node);
} else {
qDebug() << "Refusing connection from node at" << message->getSenderSockAddr()
- << "with hardware address" << nodeConnection.hardwareAddress;
+ << "with hardware address" << nodeConnection.hardwareAddress
+ << "and machine fingerprint" << nodeConnection.machineFingerprint;
}
}
-NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername,
- const QHostAddress& senderAddress, const QString& hardwareAddress) {
+NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
+ const QString& hardwareAddress, const QUuid& machineFingerprint) {
NodePermissions userPerms;
userPerms.setAll(false);
@@ -155,6 +157,11 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
#ifdef WANT_DEBUG
qDebug() << "| user-permissions: specific MAC matches, so:" << userPerms;
+#endif
+ } else if (_server->_settingsManager.hasPermissionsForMachineFingerprint(machineFingerprint)) {
+ userPerms = _server->_settingsManager.getPermissionsForMachineFingerprint(machineFingerprint);
+#ifdef WANT_DEBUG
+ qDebug(() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms;
#endif
} else if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) {
// this user comes from an IP we have in our permissions table, apply those permissions
@@ -274,13 +281,15 @@ void DomainGatekeeper::updateNodePermissions() {
HifiSockAddr connectingAddr = node->getActiveSocket() ? *node->getActiveSocket() : node->getPublicSocket();
QString hardwareAddress;
+ QUuid machineFingerprint;
DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData());
if (nodeData) {
hardwareAddress = nodeData->getHardwareAddress();
+ machineFingerprint = nodeData->getMachineFingerprint();
}
- userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress);
+ userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress, machineFingerprint);
}
node->setPermissions(userPerms);
@@ -334,6 +343,8 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
nodeData->setWalletUUID(it->second.getWalletUUID());
nodeData->setNodeVersion(it->second.getNodeVersion());
nodeData->setHardwareAddress(nodeConnection.hardwareAddress);
+ nodeData->setMachineFingerprint(nodeConnection.machineFingerprint);
+
nodeData->setWasAssigned(true);
// cleanup the PendingAssignedNodeData for this assignment now that it's connecting
@@ -396,7 +407,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
}
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, nodeConnection.senderSockAddr.getAddress(),
- nodeConnection.hardwareAddress);
+ nodeConnection.hardwareAddress, nodeConnection.machineFingerprint);
if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) {
sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.",
@@ -455,6 +466,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
// set the hardware address passed in the connect request
nodeData->setHardwareAddress(nodeConnection.hardwareAddress);
+ // set the machine fingerprint passed in the connect request
+ nodeData->setMachineFingerprint(nodeConnection.machineFingerprint);
+
// 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);
diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h
index b17d0f61a4..163f255411 100644
--- a/domain-server/src/DomainGatekeeper.h
+++ b/domain-server/src/DomainGatekeeper.h
@@ -107,8 +107,8 @@ private:
QSet _domainOwnerFriends; // keep track of friends of the domain owner
QSet _inFlightGroupMembershipsRequests; // keep track of which we've already asked for
- NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername,
- const QHostAddress& senderAddress, const QString& hardwareAddress);
+ NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
+ const QString& hardwareAddress, const QUuid& machineFingerprint);
void getGroupMemberships(const QString& username);
// void getIsGroupMember(const QString& username, const QUuid groupID);
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 3dd75a19b2..0f7923519b 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -532,6 +532,7 @@ void DomainServer::setupNodeListAndAssignments() {
// NodeList won't be available to the settings manager when it is created, so call registerListener here
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
packetReceiver.registerListener(PacketType::NodeKickRequest, &_settingsManager, "processNodeKickRequestPacket");
+ packetReceiver.registerListener(PacketType::UsernameFromIDRequest, &_settingsManager, "processUsernameFromIDRequestPacket");
// register the gatekeeper for the packets it needs to receive
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h
index ff637844f1..db41c77cf2 100644
--- a/domain-server/src/DomainServerNodeData.h
+++ b/domain-server/src/DomainServerNodeData.h
@@ -56,7 +56,10 @@ public:
void setHardwareAddress(const QString& hardwareAddress) { _hardwareAddress = hardwareAddress; }
const QString& getHardwareAddress() { return _hardwareAddress; }
-
+
+ void setMachineFingerprint(const QUuid& machineFingerprint) { _machineFingerprint = machineFingerprint; }
+ const QUuid& getMachineFingerprint() { return _machineFingerprint; }
+
void addOverrideForKey(const QString& key, const QString& value, const QString& overrideValue);
void removeOverrideForKey(const QString& key, const QString& value);
@@ -85,6 +88,7 @@ private:
NodeSet _nodeInterestSet;
QString _nodeVersion;
QString _hardwareAddress;
+ QUuid _machineFingerprint;
QString _placeName;
diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp
index 18a46e0658..05cec07f80 100644
--- a/domain-server/src/DomainServerSettingsManager.cpp
+++ b/domain-server/src/DomainServerSettingsManager.cpp
@@ -444,6 +444,9 @@ void DomainServerSettingsManager::packPermissions() {
// save settings for MAC addresses
packPermissionsForMap("permissions", _macPermissions, MAC_PERMISSIONS_KEYPATH);
+ // save settings for Machine Fingerprint
+ packPermissionsForMap("permissions", _machineFingerprintPermissions, MACHINE_FINGERPRINT_PERMISSIONS_KEYPATH);
+
// save settings for groups
packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH);
@@ -522,6 +525,18 @@ void DomainServerSettingsManager::unpackPermissions() {
}
});
+ needPack |= unpackPermissionsForKeypath(MACHINE_FINGERPRINT_PERMISSIONS_KEYPATH, &_machineFingerprintPermissions,
+ [&](NodePermissionsPointer perms){
+ // make sure that this permission row has valid machine fingerprint
+ if (QUuid(perms->getKey().first) == QUuid()) {
+ _machineFingerprintPermissions.remove(perms->getKey());
+
+ // we removed a row, so we'll need a re-pack
+ needPack = true;
+ }
+
+ });
+
needPack |= unpackPermissionsForKeypath(GROUP_PERMISSIONS_KEYPATH, &_groupPermissions,
[&](NodePermissionsPointer perms){
@@ -575,7 +590,9 @@ void DomainServerSettingsManager::unpackPermissions() {
QList> permissionsSets;
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get()
<< _groupPermissions.get() << _groupForbiddens.get()
- << _ipPermissions.get() << _macPermissions.get();
+ << _ipPermissions.get() << _macPermissions.get()
+ << _machineFingerprintPermissions.get();
+
foreach (auto permissionSet, permissionsSets) {
QHashIterator i(permissionSet);
while (i.hasNext()) {
@@ -707,9 +724,10 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointerclear(NodePermissions::Permission::canConnectToDomain);
}
- // potentially remove connect permissions for the MAC address
+ // potentially remove connect permissions for the MAC address and machine fingerprint
DomainServerNodeData* nodeData = reinterpret_cast(matchingNode->getLinkedData());
if (nodeData) {
+ // mac address first
NodePermissionsKey macAddressKey(nodeData->getHardwareAddress(), 0);
bool hadMACPermissions = hasPermissionsForMAC(nodeData->getHardwareAddress());
@@ -721,6 +739,18 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointerclear(NodePermissions::Permission::canConnectToDomain);
}
+
+ // now for machine fingerprint
+ NodePermissionsKey machineFingerprintKey(nodeData->getMachineFingerprint().toString(), 0);
+
+ bool hadFingerprintPermissions = hasPermissionsForMachineFingerprint(nodeData->getMachineFingerprint());
+
+ auto fingerprintPermissions = _machineFingerprintPermissions[machineFingerprintKey];
+
+ if (!hadFingerprintPermissions || fingerprintPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
+ newPermissions = true;
+ fingerprintPermissions->clear(NodePermissions::Permission::canConnectToDomain);
+ }
}
}
@@ -748,6 +778,45 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer message, SharedNodePointer sendingNode) {
+ // Before we do any processing on this packet, make sure it comes from a node that is allowed to kick (is an admin)
+ if (sendingNode->getCanKick()) {
+ // From the packet, pull the UUID we're identifying
+ QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
+
+ if (!nodeUUID.isNull()) {
+ // First, make sure we actually have a node with this UUID
+ auto limitedNodeList = DependencyManager::get();
+ auto matchingNode = limitedNodeList->nodeWithUUID(nodeUUID);
+
+ // If we do have a matching node...
+ if (matchingNode) {
+ // It's time to figure out the username
+ QString verifiedUsername = matchingNode->getPermissions().getVerifiedUserName();
+
+ // Setup the packet
+ auto usernameFromIDReplyPacket = NLPacket::create(PacketType::UsernameFromIDReply);
+ usernameFromIDReplyPacket->write(nodeUUID.toRfc4122());
+ usernameFromIDReplyPacket->writeString(verifiedUsername);
+
+ qDebug() << "Sending username" << verifiedUsername << "associated with node" << nodeUUID;
+
+ // Ship it!
+ limitedNodeList->sendPacket(std::move(usernameFromIDReplyPacket), *sendingNode);
+ } else {
+ qWarning() << "Node username request received for unknown node. Refusing to process.";
+ }
+ } else {
+ qWarning() << "Node username request received for invalid node ID. Refusing to process.";
+ }
+
+ } else {
+ qWarning() << "Refusing to process a username request packet from node" << uuidStringWithoutCurlyBraces(sendingNode->getUUID())
+ << "that does not have kick permissions.";
+ }
+}
+
QStringList DomainServerSettingsManager::getAllNames() const {
QStringList result;
foreach (auto key, _agentPermissions.keys()) {
@@ -795,6 +864,16 @@ NodePermissions DomainServerSettingsManager::getPermissionsForMAC(const QString&
return nullPermissions;
}
+NodePermissions DomainServerSettingsManager::getPermissionsForMachineFingerprint(const QUuid& machineFingerprint) const {
+ NodePermissionsKey fingerprintKey = NodePermissionsKey(machineFingerprint.toString(), 0);
+ if (_machineFingerprintPermissions.contains(fingerprintKey)) {
+ return *(_machineFingerprintPermissions[fingerprintKey].get());
+ }
+ NodePermissions nullPermissions;
+ nullPermissions.setAll(false);
+ return nullPermissions;
+}
+
NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QString& groupName, QUuid rankID) const {
NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rankID);
if (_groupPermissions.contains(groupRankKey)) {
diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h
index 06ad7decd1..2b5f9518a0 100644
--- a/domain-server/src/DomainServerSettingsManager.h
+++ b/domain-server/src/DomainServerSettingsManager.h
@@ -32,6 +32,7 @@ const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permission
const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions";
const QString IP_PERMISSIONS_KEYPATH = "security.ip_permissions";
const QString MAC_PERMISSIONS_KEYPATH = "security.mac_permissions";
+const QString MACHINE_FINGERPRINT_PERMISSIONS_KEYPATH = "security.machine_fingerprint_permissions";
const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions";
const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens";
@@ -70,6 +71,10 @@ public:
bool hasPermissionsForMAC(const QString& macAddress) const { return _macPermissions.contains(macAddress, 0); }
NodePermissions getPermissionsForMAC(const QString& macAddress) const;
+ // these give access to permissions for specific machine fingerprints from the domain-server settings page
+ bool hasPermissionsForMachineFingerprint(const QUuid& machineFingerprint) { return _machineFingerprintPermissions.contains(machineFingerprint.toString(), 0); }
+ NodePermissions getPermissionsForMachineFingerprint(const QUuid& machineFingerprint) const;
+
// these give access to permissions for specific groups from the domain-server settings page
bool havePermissionsForGroup(const QString& groupName, QUuid rankID) const {
return _groupPermissions.contains(groupName, rankID);
@@ -113,6 +118,7 @@ public slots:
private slots:
void processSettingsRequestPacket(QSharedPointer message);
void processNodeKickRequestPacket(QSharedPointer message, SharedNodePointer sendingNode);
+ void processUsernameFromIDRequestPacket(QSharedPointer message, SharedNodePointer sendingNode);
private:
QStringList _argumentList;
@@ -151,6 +157,7 @@ private:
NodePermissionsMap _ipPermissions; // permissions granted by node IP address
NodePermissionsMap _macPermissions; // permissions granted by node MAC address
+ NodePermissionsMap _machineFingerprintPermissions; // permissions granted by Machine Fingerprint
NodePermissionsMap _groupPermissions; // permissions granted by membership to specific groups
NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group
diff --git a/domain-server/src/NodeConnectionData.cpp b/domain-server/src/NodeConnectionData.cpp
index 93d6802d84..0a3782d79b 100644
--- a/domain-server/src/NodeConnectionData.cpp
+++ b/domain-server/src/NodeConnectionData.cpp
@@ -32,6 +32,9 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c
// read the hardware address sent by the client
dataStream >> newHeader.hardwareAddress;
+
+ // now the machine fingerprint
+ dataStream >> newHeader.machineFingerprint;
}
dataStream >> newHeader.nodeType
diff --git a/domain-server/src/NodeConnectionData.h b/domain-server/src/NodeConnectionData.h
index bcbbdf0a40..dd9ca6b650 100644
--- a/domain-server/src/NodeConnectionData.h
+++ b/domain-server/src/NodeConnectionData.h
@@ -29,6 +29,7 @@ public:
QList interestList;
QString placeName;
QString hardwareAddress;
+ QUuid machineFingerprint;
QByteArray protocolVersion;
};
diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml
index 382b8fc962..7a8dc4722e 100644
--- a/interface/resources/qml/hifi/Pal.qml
+++ b/interface/resources/qml/hifi/Pal.qml
@@ -72,6 +72,24 @@ Rectangle {
table.selection.deselect(userIndex);
}
break;
+ // Received an "updateUsername()" request from the JS
+ case 'updateUsername':
+ // The User ID (UUID) is the first parameter in the message.
+ var userId = message.params[0];
+ // The text that goes in the userName field is the second parameter in the message.
+ var userName = message.params[1];
+ // If the userId is empty, we're updating "myData".
+ if (!userId) {
+ myData.userName = userName;
+ myCard.userName = userName; // Defensive programming
+ } else {
+ // Get the index in userModel and userData associated with the passed UUID
+ var userIndex = findSessionIndex(userId);
+ // Set the userName appropriately
+ userModel.get(userIndex).userName = userName;
+ userData[userIndex].userName = userName; // Defensive programming
+ }
+ break;
default:
console.log('Unrecognized message:', JSON.stringify(message));
}
diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt
index 8ef67cb2a4..288e98d5a5 100644
--- a/libraries/networking/CMakeLists.txt
+++ b/libraries/networking/CMakeLists.txt
@@ -17,6 +17,7 @@ find_package(TBB REQUIRED)
if (APPLE)
find_library(FRAMEWORK_IOKIT IOKit)
+ find_library(CORE_FOUNDATION CoreFoundation)
endif ()
if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include")
@@ -32,7 +33,7 @@ target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES} ${TBB_LIBRARIES})
# IOKit is needed for getting machine fingerprint
if (APPLE)
- target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT})
+ target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT} ${CORE_FOUNDATION})
endif (APPLE)
# libcrypto uses dlopen in libdl
diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp
index 42a617e93d..70a2f8ab2c 100644
--- a/libraries/networking/src/FingerprintUtils.cpp
+++ b/libraries/networking/src/FingerprintUtils.cpp
@@ -10,8 +10,13 @@
//
#include "FingerprintUtils.h"
+
#include
+
#include
+#include
+#include
+
#ifdef Q_OS_WIN
#include
#include
@@ -44,7 +49,15 @@ QString FingerprintUtils::getMachineFingerprintString() {
HRESULT hres;
IWbemLocator *pLoc = NULL;
- // initialize com
+ // initialize com. Interface already does, but other
+ // users of this lib don't necessarily do so.
+ hres = CoInitializeEx(0, COINIT_MULTITHREADED);
+ if (FAILED(hres)) {
+ qDebug() << "Failed to initialize COM library!";
+ return uuidString;
+ }
+
+ // initialize WbemLocator
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
@@ -164,6 +177,11 @@ QUuid FingerprintUtils::getMachineFingerprint() {
// any errors in getting the string
QUuid uuid(uuidString);
if (uuid == QUuid()) {
+ // if you cannot read a fallback key cuz we aren't saving them, just generate one for
+ // this session and move on
+ if (DependencyManager::get().isNull()) {
+ return QUuid::createUuid();
+ }
// read fallback key (if any)
Settings settings;
uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString());
diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp
index 29b7ea0385..f76189b13a 100644
--- a/libraries/networking/src/NodeList.cpp
+++ b/libraries/networking/src/NodeList.cpp
@@ -27,6 +27,7 @@
#include "AddressManager.h"
#include "Assignment.h"
#include "HifiSockAddr.h"
+#include "FingerprintUtils.h"
#include "NetworkLogging.h"
#include "udt/PacketHeaders.h"
@@ -127,6 +128,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket");
packetReceiver.registerListener(PacketType::DomainServerPathResponse, this, "processDomainServerPathResponse");
packetReceiver.registerListener(PacketType::DomainServerRemovedNode, this, "processDomainServerRemovedNode");
+ packetReceiver.registerListener(PacketType::UsernameFromIDReply, this, "processUsernameFromIDReply");
}
qint64 NodeList::sendStats(QJsonObject statsObject, HifiSockAddr destination) {
@@ -370,6 +372,10 @@ void NodeList::sendDomainServerCheckIn() {
}
packetStream << hardwareAddress;
+
+ // add in machine fingerprint
+ QUuid machineFingerprint = FingerprintUtils::getMachineFingerprint();
+ packetStream << machineFingerprint;
}
// pack our data to send to the domain-server including
@@ -889,3 +895,36 @@ void NodeList::muteNodeBySessionID(const QUuid& nodeID) {
}
}
+
+void NodeList::requestUsernameFromSessionID(const QUuid& nodeID) {
+ // send a request to domain-server to get the username associated with the given session ID
+ if (getThisNodeCanKick() || nodeID.isNull()) {
+ // setup the packet
+ auto usernameFromIDRequestPacket = NLPacket::create(PacketType::UsernameFromIDRequest, NUM_BYTES_RFC4122_UUID, true);
+
+ // write the node ID to the packet
+ if (nodeID.isNull()) {
+ usernameFromIDRequestPacket->write(getSessionUUID().toRfc4122());
+ } else {
+ usernameFromIDRequestPacket->write(nodeID.toRfc4122());
+ }
+
+ qDebug() << "Sending packet to get username of node" << uuidStringWithoutCurlyBraces(nodeID);
+
+ sendPacket(std::move(usernameFromIDRequestPacket), _domainHandler.getSockAddr());
+ } else {
+ qWarning() << "You do not have permissions to kick in this domain."
+ << "Request to get the username of node" << uuidStringWithoutCurlyBraces(nodeID) << "will not be sent";
+ }
+}
+
+void NodeList::processUsernameFromIDReply(QSharedPointer message) {
+ // read the UUID from the packet
+ QString nodeUUIDString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString();
+ // read the username from the packet
+ QString username = message->readString();
+
+ qDebug() << "Got username" << username << "for node" << nodeUUIDString;
+
+ emit usernameFromIDReply(nodeUUIDString, username);
+}
diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h
index 9a9b85b851..d10c790f45 100644
--- a/libraries/networking/src/NodeList.h
+++ b/libraries/networking/src/NodeList.h
@@ -81,6 +81,7 @@ public:
void kickNodeBySessionID(const QUuid& nodeID);
void muteNodeBySessionID(const QUuid& nodeID);
+ void requestUsernameFromSessionID(const QUuid& nodeID);
public slots:
void reset();
@@ -99,6 +100,8 @@ public slots:
void processICEPingPacket(QSharedPointer message);
+ void processUsernameFromIDReply(QSharedPointer message);
+
#if (PR_BUILD || DEV_BUILD)
void toggleSendNewerDSConnectVersion(bool shouldSendNewerVersion) { _shouldSendNewerVersion = shouldSendNewerVersion; }
#endif
@@ -108,6 +111,7 @@ signals:
void receivedDomainServerList();
void ignoredNode(const QUuid& nodeID);
void ignoreRadiusEnabledChanged(bool isIgnored);
+ void usernameFromIDReply(const QString& nodeID, const QString& username);
private slots:
void stopKeepalivePingTimer();
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index 79776e2d54..339f41a5e8 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -26,8 +26,8 @@ const QSet NON_VERIFIED_PACKETS = QSet()
<< PacketType::NodeJsonStats << PacketType::EntityQuery
<< PacketType::OctreeDataNack << PacketType::EntityEditNack
<< PacketType::DomainListRequest << PacketType::StopNode
- << PacketType::DomainDisconnectRequest << PacketType::NodeKickRequest
- << PacketType::NodeMuteRequest;
+ << PacketType::DomainDisconnectRequest << PacketType::UsernameFromIDRequest
+ << PacketType::NodeKickRequest << PacketType::NodeMuteRequest;
const QSet NON_SOURCED_PACKETS = QSet()
<< PacketType::StunResponse << PacketType::CreateAssignment << PacketType::RequestAssignment
@@ -39,12 +39,12 @@ const QSet NON_SOURCED_PACKETS = QSet()
<< PacketType::ICEServerPeerInformation << PacketType::ICEServerQuery << PacketType::ICEServerHeartbeat
<< PacketType::ICEServerHeartbeatACK << PacketType::ICEPing << PacketType::ICEPingReply
<< PacketType::ICEServerHeartbeatDenied << PacketType::AssignmentClientStatus << PacketType::StopNode
- << PacketType::DomainServerRemovedNode;
+ << PacketType::DomainServerRemovedNode << PacketType::UsernameFromIDReply;
PacketVersion versionForPacketType(PacketType packetType) {
switch (packetType) {
case PacketType::DomainList:
- return static_cast(DomainListVersion::PermissionsGrid);
+ return static_cast(DomainListVersion::GetUsernameFromUUIDSupport);
case PacketType::EntityAdd:
case PacketType::EntityEdit:
case PacketType::EntityData:
@@ -67,7 +67,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
return static_cast(DomainConnectionDeniedVersion::IncludesExtraInfo);
case PacketType::DomainConnectRequest:
- return static_cast(DomainConnectRequestVersion::HasMACAddress);
+ return static_cast(DomainConnectRequestVersion::HasMachineFingerprint);
case PacketType::DomainServerAddedNode:
return static_cast(DomainServerAddedNodeVersion::PermissionsGrid);
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index 778cce4c8d..ef0c63c9ce 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -101,7 +101,9 @@ public:
NodeKickRequest,
NodeMuteRequest,
RadiusIgnoreRequest,
- LAST_PACKET_TYPE = RadiusIgnoreRequest
+ UsernameFromIDRequest,
+ UsernameFromIDReply,
+ LAST_PACKET_TYPE = UsernameFromIDReply
};
};
@@ -211,7 +213,8 @@ enum class DomainConnectRequestVersion : PacketVersion {
NoHostname = 17,
HasHostname,
HasProtocolVersions,
- HasMACAddress
+ HasMACAddress,
+ HasMachineFingerprint
};
enum class DomainConnectionDeniedVersion : PacketVersion {
@@ -227,7 +230,8 @@ enum class DomainServerAddedNodeVersion : PacketVersion {
enum class DomainListVersion : PacketVersion {
PrePermissionsGrid = 18,
- PermissionsGrid
+ PermissionsGrid,
+ GetUsernameFromUUIDSupport
};
enum class AudioVersion : PacketVersion {
diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp
index 83181f7509..191952e354 100644
--- a/libraries/script-engine/src/UsersScriptingInterface.cpp
+++ b/libraries/script-engine/src/UsersScriptingInterface.cpp
@@ -18,6 +18,7 @@ UsersScriptingInterface::UsersScriptingInterface() {
auto nodeList = DependencyManager::get();
connect(nodeList.data(), &LimitedNodeList::canKickChanged, this, &UsersScriptingInterface::canKickChanged);
connect(nodeList.data(), &NodeList::ignoreRadiusEnabledChanged, this, &UsersScriptingInterface::ignoreRadiusEnabledChanged);
+ connect(nodeList.data(), &NodeList::usernameFromIDReply, this, &UsersScriptingInterface::usernameFromIDReply);
}
void UsersScriptingInterface::ignore(const QUuid& nodeID) {
@@ -35,6 +36,11 @@ void UsersScriptingInterface::mute(const QUuid& nodeID) {
DependencyManager::get()->muteNodeBySessionID(nodeID);
}
+void UsersScriptingInterface::requestUsernameFromID(const QUuid& nodeID) {
+ // ask the Domain Server via the NodeList for the username associated with the given session ID
+ DependencyManager::get()->requestUsernameFromSessionID(nodeID);
+}
+
bool UsersScriptingInterface::getCanKick() {
// ask the NodeList to return our ability to kick
return DependencyManager::get()->getThisNodeCanKick();
diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h
index 87e8c7e0d8..1936a2e914 100644
--- a/libraries/script-engine/src/UsersScriptingInterface.h
+++ b/libraries/script-engine/src/UsersScriptingInterface.h
@@ -51,6 +51,13 @@ public slots:
*/
void mute(const QUuid& nodeID);
+ /**jsdoc
+ * Returns a string containing the username associated with the given Avatar UUID
+ * @function Users.getUsernameFromID
+ * @param {nodeID} nodeID The node or session ID of the user whose username you want.
+ */
+ void requestUsernameFromID(const QUuid& nodeID);
+
/**jsdoc
* Returns `true` if the DomainServer will allow this Node/Avatar to make kick
* @function Users.getCanKick
@@ -92,6 +99,12 @@ signals:
* @function Users.enteredIgnoreRadius
*/
void enteredIgnoreRadius();
+
+ /**jsdoc
+ * Notifies scripts of the username associated with a UUID.
+ * @function Users.enteredIgnoreRadius
+ */
+ void usernameFromIDReply(const QString& nodeID, const QString& username);
};
diff --git a/scripts/system/pal.js b/scripts/system/pal.js
index 61d3bb00e8..4d620f2a85 100644
--- a/scripts/system/pal.js
+++ b/scripts/system/pal.js
@@ -118,9 +118,15 @@ function populateUserList() {
var avatar = AvatarList.getAvatar(id);
var avatarPalDatum = {
displayName: avatar.sessionDisplayName,
- userName: "fakeAcct" + (id || "Me"),
+ userName: '',
sessionId: id || ''
};
+ // If the current user is an admin OR
+ // they're requesting their own username ("id" is blank)...
+ if (Users.canKick || !id) {
+ // Request the username from the given UUID
+ Users.requestUsernameFromID(id);
+ }
data.push(avatarPalDatum);
if (id) { // No overlay for ourself.
addAvatarNode(id);
@@ -129,6 +135,23 @@ function populateUserList() {
});
pal.sendToQml({method: 'users', params: data});
}
+
+// The function that handles the reply from the server
+function usernameFromIDReply(id, username) {
+ var data;
+ // If the ID we've received is our ID...
+ if (AvatarList.getAvatar('').sessionUUID === id) {
+ // Set the data to contain specific strings.
+ data = ['', username + ' (hidden)']
+ } else {
+ // Set the data to contain the ID and the username+ID concat string.
+ data = [id, username + '/' + id];
+ }
+ print('Username Data:', JSON.stringify(data));
+ // Ship the data off to QML
+ pal.sendToQml({ method: 'updateUsername', params: data });
+}
+
var pingPong = true;
function updateOverlays() {
var eye = Camera.position;
@@ -249,6 +272,7 @@ function onVisibileChanged() {
button.clicked.connect(onClicked);
pal.visibleChanged.connect(onVisibileChanged);
pal.closed.connect(off);
+Users.usernameFromIDReply.connect(usernameFromIDReply);
//
// Cleanup.
@@ -258,6 +282,7 @@ Script.scriptEnding.connect(function () {
toolBar.removeButton(buttonName);
pal.visibleChanged.disconnect(onVisibileChanged);
pal.closed.disconnect(off);
+ Users.usernameFromIDReply.disconnect(usernameFromIDReply);
off();
});