diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index f8c45b792a..daeb4a8f1f 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -56,6 +56,7 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership
connect(nodeList.data(), &NodeList::canRezCertifiedChanged, this, &EntityScriptingInterface::canRezCertifiedChanged);
connect(nodeList.data(), &NodeList::canRezTmpCertifiedChanged, this, &EntityScriptingInterface::canRezTmpCertifiedChanged);
connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged);
+ connect(nodeList.data(), &NodeList::canGetAndSetPrivateUserDataChanged, this, &EntityScriptingInterface::canGetAndSetPrivateUserDataChanged);
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket");
diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h
index 38b73aaf45..c072dedaf9 100644
--- a/libraries/entities/src/EntityScriptingInterface.h
+++ b/libraries/entities/src/EntityScriptingInterface.h
@@ -236,6 +236,14 @@ public slots:
*/
Q_INVOKABLE bool canReplaceContent();
+ /**jsdoc
+ * Check whether or not you can get and set private user data.
+ * @function Entities.canGetAndSetPrivateUserData
+ * @returns {boolean} true
if the domain server will allow the user to get and set private user data,
+ * otherwise false
.
+ */
+ Q_INVOKABLE bool canGetAndSetPrivateUserData();
+
/**jsdoc
*
How an entity is sent over the wire.
*
@@ -1861,6 +1869,15 @@ signals:
*/
void canWriteAssetsChanged(bool canWriteAssets);
+ /**jsdoc
+ * Triggered when your ability to get and set private user data changes.
+ * @function Entities.canGetAndSetPrivateUserDataChanged
+ * @param {boolean} canGetAndSetPrivateUserData - true
if you can change the privateUserData
property of an entity,
+ * otherwise false
.
+ * @returns {Signal}
+ */
+ void canGetAndSetPrivateUserDataChanged(bool canGetAndSetPrivateUserData);
+
/**jsdoc
* Triggered when a mouse button is clicked while the mouse cursor is on an entity, or a controller trigger is fully
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index 806f6189e2..260d044693 100644
--- a/libraries/entities/src/EntityTree.cpp
+++ b/libraries/entities/src/EntityTree.cpp
@@ -1780,6 +1780,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
bool suppressDisallowedClientScript = false;
bool suppressDisallowedServerScript = false;
+ bool suppressDisallowedPrivateUserData = false;
bool isPhysics = message.getType() == PacketType::EntityPhysics;
_totalEditMessages++;
@@ -1869,6 +1870,27 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
}
}
+ if (!properties.getPrivateUserData().isEmpty()) {
+ if (!senderNode->getCanGetAndSetPrivateUserData()) {
+ if (wantEditLogging()) {
+ qCDebug(entities) << "User [" << senderNode->getUUID()
+ << "] is attempting to set private user data but user isn't allowed; edit rejected...";
+ }
+
+ // If this was an add, we also want to tell the client that sent this edit that the entity was not added.
+ if (isAdd) {
+ // Make sure we didn't already need to send back a delete because the client script failed
+ // the whitelist check
+ if (!wasDeletedBecauseOfClientScript) {
+ QWriteLocker locker(&_recentlyDeletedEntitiesLock);
+ _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
+ validEditPacket = false;
+ }
+ } else {
+ suppressDisallowedPrivateUserData = true;
+ }
+ }
+ }
}
if (!isClone) {
@@ -1923,6 +1945,11 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
properties.setServerScripts(existingEntity->getServerScripts());
}
+ if (suppressDisallowedPrivateUserData) {
+ bumpTimestamp(properties);
+ properties.setPrivateUserData(existingEntity->getPrivateUserData());
+ }
+
// if the EntityItem exists, then update it
startLogging = usecTimestampNow();
if (wantEditLogging()) {
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 34eecf5ee8..c2f606e7dd 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -202,7 +202,6 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) {
emit canGetAndSetPrivateUserDataChanged(_permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData));
}
}
-}
void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) {
if (QThread::currentThread() != thread()) {
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index 8c97b3ca6a..5de7e56e47 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -135,7 +135,6 @@ public:
AudioSoloRequest,
BulkAvatarTraitsAck,
StopInjector,
- PrivateUserData,
NUM_PACKET_TYPE
};
@@ -270,6 +269,7 @@ enum class EntityVersion : PacketVersion {
CertificateTypeProperty,
DisableWebMedia,
ParticleShapeType,
+ PrivateUserData,
// Add new versions above here
NUM_PACKET_TYPE,