Merge pull request #4239 from sethalves/allowed-editors

Allowed editors domain setting
This commit is contained in:
Brad Hefta-Gaub 2015-02-06 11:06:24 -08:00
commit ea0c828d7b
13 changed files with 94 additions and 34 deletions

View file

@ -73,6 +73,20 @@
"can_set": true "can_set": true
} }
] ]
},
{
"name": "allowed_editors",
"type": "table",
"label": "Allowed Editors",
"help": "List the High Fidelity names for people you want to be able lock or unlock entities in this domain.<br/>An empty list means everyone.",
"numbered": false,
"columns": [
{
"name": "username",
"label": "Username",
"can_set": true
}
]
} }
] ]
}, },
@ -433,4 +447,4 @@
} }
] ]
} }
] ]

View file

@ -41,6 +41,11 @@ int const DomainServer::EXIT_CODE_REBOOT = 234923;
const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io"; const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io";
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
DomainServer::DomainServer(int argc, char* argv[]) : DomainServer::DomainServer(int argc, char* argv[]) :
QCoreApplication(argc, argv), QCoreApplication(argc, argv),
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), _httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
@ -638,10 +643,16 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
// we got a packetUUID we didn't recognize, just add the node // we got a packetUUID we didn't recognize, just add the node
nodeUUID = QUuid::createUuid(); nodeUUID = QUuid::createUuid();
} }
SharedNodePointer newNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode(nodeUUID, nodeType, // if this user is in the editors list (or if the editors list is empty) set the user's node's canAdjustLocks to true
publicSockAddr, localSockAddr); const QVariant* allowedEditorsVariant =
valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
bool canAdjustLocks = allowedEditors.isEmpty() || allowedEditors.contains(username);
SharedNodePointer newNode =
DependencyManager::get<LimitedNodeList>()->addOrUpdateNode(nodeUUID, nodeType,
publicSockAddr, localSockAddr, canAdjustLocks);
// when the newNode is created the linked data is also created // when the newNode is created the linked data is also created
// if this was a static assignment set the UUID, set the sendingSockAddr // if this was a static assignment set the UUID, set the sendingSockAddr
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData()); DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
@ -663,7 +674,6 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
} }
} }
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
bool DomainServer::shouldAllowConnectionFromNode(const QString& username, bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
const QByteArray& usernameSignature, const QByteArray& usernameSignature,
@ -842,6 +852,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
// always send the node their own UUID back // always send the node their own UUID back
QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append); QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append);
broadcastDataStream << node->getUUID(); broadcastDataStream << node->getUUID();
broadcastDataStream << node->getCanAdjustLocks();
int numBroadcastPacketLeadBytes = broadcastDataStream.device()->pos(); int numBroadcastPacketLeadBytes = broadcastDataStream.device()->pos();

View file

@ -26,6 +26,13 @@ void EntityScriptingInterface::queueEntityMessage(PacketType packetType,
getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties); getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties);
} }
bool EntityScriptingInterface::canAdjustLocks() {
auto nodeList = DependencyManager::get<NodeList>();
return nodeList->getThisNodeCanAdjustLocks();
}
EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& properties) { EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {
// The application will keep track of creatorTokenID // The application will keep track of creatorTokenID
@ -108,7 +115,7 @@ EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const E
// the actual id, because we can edit out local entities just with creatorTokenID // the actual id, because we can edit out local entities just with creatorTokenID
if (_entityTree) { if (_entityTree) {
_entityTree->lockForWrite(); _entityTree->lockForWrite();
_entityTree->updateEntity(entityID, properties); _entityTree->updateEntity(entityID, properties, canAdjustLocks());
_entityTree->unlock(); _entityTree->unlock();
} }

View file

@ -61,6 +61,10 @@ public:
EntityTree* getEntityTree(EntityTree*) { return _entityTree; } EntityTree* getEntityTree(EntityTree*) { return _entityTree; }
public slots: public slots:
// returns true if the DomainServer will allow this Node/Avatar to make changes
Q_INVOKABLE bool canAdjustLocks();
/// adds a model with the specific properties /// adds a model with the specific properties
Q_INVOKABLE EntityItemID addEntity(const EntityItemProperties& properties); Q_INVOKABLE EntityItemID addEntity(const EntityItemProperties& properties);

View file

@ -93,7 +93,7 @@ void EntityTree::postAddEntity(EntityItem* entity) {
emit addingEntity(entity->getEntityItemID()); emit addingEntity(entity->getEntityItemID());
} }
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool allowLockChange) {
EntityTreeElement* containingElement = getContainingElement(entityID); EntityTreeElement* containingElement = getContainingElement(entityID);
if (!containingElement) { if (!containingElement) {
qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID; qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
@ -106,21 +106,27 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
return false; return false;
} }
return updateEntityWithElement(existingEntity, properties, containingElement); return updateEntityWithElement(existingEntity, properties, containingElement, allowLockChange);
} }
bool EntityTree::updateEntity(EntityItem* entity, const EntityItemProperties& properties) { bool EntityTree::updateEntity(EntityItem* entity, const EntityItemProperties& properties, bool allowLockChange) {
EntityTreeElement* containingElement = getContainingElement(entity->getEntityItemID()); EntityTreeElement* containingElement = getContainingElement(entity->getEntityItemID());
if (!containingElement) { if (!containingElement) {
qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entity-->element lookup failed!!! entityID=" qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entity-->element lookup failed!!! entityID="
<< entity->getEntityItemID(); << entity->getEntityItemID();
return false; return false;
} }
return updateEntityWithElement(entity, properties, containingElement); return updateEntityWithElement(entity, properties, containingElement, allowLockChange);
} }
bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties, bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties,
EntityTreeElement* containingElement) { EntityTreeElement* containingElement, bool allowLockChange) {
if (!allowLockChange && (entity->getLocked() != properties.getLocked())) {
qDebug() << "Refusing disallowed lock adjustment.";
return false;
}
// enforce support for locked entities. If an entity is currently locked, then the only // enforce support for locked entities. If an entity is currently locked, then the only
// property we allow you to change is the locked property. // property we allow you to change is the locked property.
if (entity->getLocked()) { if (entity->getLocked()) {
@ -586,7 +592,7 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
// if the EntityItem exists, then update it // if the EntityItem exists, then update it
if (existingEntity) { if (existingEntity) {
updateEntity(entityItemID, properties); updateEntity(entityItemID, properties, senderNode->getCanAdjustLocks());
existingEntity->markAsChangedOnServer(); existingEntity->markAsChangedOnServer();
} else { } else {
qDebug() << "User attempted to edit an unknown entity. ID:" << entityItemID; qDebug() << "User attempted to edit an unknown entity. ID:" << entityItemID;

View file

@ -86,10 +86,10 @@ public:
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties); EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
// use this method if you only know the entityID // use this method if you only know the entityID
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties); bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool allowLockChange);
// use this method if you have a pointer to the entity (avoid an extra entity lookup) // use this method if you have a pointer to the entity (avoid an extra entity lookup)
bool updateEntity(EntityItem* entity, const EntityItemProperties& properties); bool updateEntity(EntityItem* entity, const EntityItemProperties& properties, bool allowLockChange);
void deleteEntity(const EntityItemID& entityID, bool force = false); void deleteEntity(const EntityItemID& entityID, bool force = false);
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false); void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false);
@ -162,7 +162,7 @@ private:
void processRemovedEntities(const DeleteEntityOperator& theOperator); void processRemovedEntities(const DeleteEntityOperator& theOperator);
bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties, bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties,
EntityTreeElement* containingElement); EntityTreeElement* containingElement, bool allowLockChange);
static bool findNearPointOperation(OctreeElement* element, void* extraData); static bool findNearPointOperation(OctreeElement* element, void* extraData);
static bool findInSphereOperation(OctreeElement* element, void* extraData); static bool findInSphereOperation(OctreeElement* element, void* extraData);
static bool findInCubeOperation(OctreeElement* element, void* extraData); static bool findInCubeOperation(OctreeElement* element, void* extraData);

View file

@ -411,7 +411,8 @@ 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 canAdjustLocks) {
NodeHash::const_iterator it = _nodeHash.find(uuid); NodeHash::const_iterator it = _nodeHash.find(uuid);
if (it != _nodeHash.end()) { if (it != _nodeHash.end()) {
@ -419,11 +420,12 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
matchingNode->setPublicSocket(publicSocket); matchingNode->setPublicSocket(publicSocket);
matchingNode->setLocalSocket(localSocket); matchingNode->setLocalSocket(localSocket);
matchingNode->setCanAdjustLocks(canAdjustLocks);
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); Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, canAdjustLocks);
SharedNodePointer newNodePointer(newNode); SharedNodePointer newNodePointer(newNode);
_nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer)); _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer));

View file

@ -76,6 +76,9 @@ class LimitedNodeList : public QObject, public Dependency {
public: public:
const QUuid& getSessionUUID() const { return _sessionUUID; } const QUuid& getSessionUUID() const { return _sessionUUID; }
void setSessionUUID(const QUuid& sessionUUID); void setSessionUUID(const QUuid& sessionUUID);
bool getThisNodeCanAdjustLocks() { return _thisNodeCanAdjustLocks; }
void setThisNodeCanAdjustLocks(bool canAdjustLocks) { _thisNodeCanAdjustLocks = canAdjustLocks; }
void rebindNodeSocket(); void rebindNodeSocket();
QUdpSocket& getNodeSocket() { return _nodeSocket; } QUdpSocket& getNodeSocket() { return _nodeSocket; }
@ -106,7 +109,7 @@ public:
SharedNodePointer sendingNodeForPacket(const QByteArray& packet); SharedNodePointer sendingNodeForPacket(const QByteArray& packet);
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 canAdjustLocks);
const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; } const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; }
const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; } const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; }
@ -201,6 +204,7 @@ protected:
void handleNodeKill(const SharedNodePointer& node); void handleNodeKill(const SharedNodePointer& node);
QUuid _sessionUUID; QUuid _sessionUUID;
bool _thisNodeCanAdjustLocks;
NodeHash _nodeHash; NodeHash _nodeHash;
QReadWriteLock _nodeMutex; QReadWriteLock _nodeMutex;
QUdpSocket _nodeSocket; QUdpSocket _nodeSocket;

View file

@ -41,8 +41,9 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
return matchedTypeName != TypeNameHash.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME; return matchedTypeName != TypeNameHash.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME;
} }
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) : Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
NetworkPeer(uuid, publicSocket, localSocket), const HifiSockAddr& localSocket, bool canAdjustLocks) :
NetworkPeer(uuid, publicSocket, localSocket),
_type(type), _type(type),
_activeSocket(NULL), _activeSocket(NULL),
_symmetricSocket(), _symmetricSocket(),
@ -52,7 +53,8 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
_pingMs(-1), // "Uninitialized" _pingMs(-1), // "Uninitialized"
_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
_canAdjustLocks(canAdjustLocks)
{ {
} }
@ -131,6 +133,7 @@ QDataStream& operator<<(QDataStream& out, const Node& node) {
out << node._uuid; out << node._uuid;
out << node._publicSocket; out << node._publicSocket;
out << node._localSocket; out << node._localSocket;
out << node._canAdjustLocks;
return out; return out;
} }
@ -140,6 +143,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._canAdjustLocks;
return in; return in;
} }

View file

@ -45,7 +45,8 @@ namespace NodeType {
class Node : public NetworkPeer { class Node : public NetworkPeer {
Q_OBJECT Q_OBJECT
public: public:
Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); Node(const QUuid& uuid, NodeType_t type,
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, bool canAdjustLocks);
~Node(); ~Node();
bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; } bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; }
@ -76,6 +77,9 @@ public:
virtual void setSymmetricSocket(const HifiSockAddr& symmetricSocket); virtual void setSymmetricSocket(const HifiSockAddr& symmetricSocket);
const HifiSockAddr* getActiveSocket() const { return _activeSocket; } const HifiSockAddr* getActiveSocket() const { return _activeSocket; }
void setCanAdjustLocks(bool canAdjustLocks) { _canAdjustLocks = canAdjustLocks; }
bool getCanAdjustLocks() { return _canAdjustLocks; }
void activatePublicSocket(); void activatePublicSocket();
void activateLocalSocket(); void activateLocalSocket();
@ -101,6 +105,7 @@ private:
int _clockSkewUsec; int _clockSkewUsec;
QMutex _mutex; QMutex _mutex;
MovingPercentile _clockSkewMovingPercentile; MovingPercentile _clockSkewMovingPercentile;
bool _canAdjustLocks;
}; };
QDebug operator<<(QDebug debug, const Node &message); QDebug operator<<(QDebug debug, const Node &message);

View file

@ -370,13 +370,6 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
int readNodes = 0; int readNodes = 0;
// setup variables to read into from QDataStream
qint8 nodeType;
QUuid nodeUUID, connectionUUID;
HifiSockAddr nodePublicSocket;
HifiSockAddr nodeLocalSocket;
QDataStream packetStream(packet); QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet)); packetStream.skipRawData(numBytesForPacketHeader(packet));
@ -385,10 +378,20 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
QUuid newUUID; QUuid newUUID;
packetStream >> newUUID; packetStream >> newUUID;
setSessionUUID(newUUID); setSessionUUID(newUUID);
bool thisNodeCanAdjustLocks;
packetStream >> thisNodeCanAdjustLocks;
setThisNodeCanAdjustLocks(thisNodeCanAdjustLocks);
// pull each node in the packet // pull each node in the packet
while(packetStream.device()->pos() < packet.size()) { while(packetStream.device()->pos() < packet.size()) {
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket; // setup variables to read into from QDataStream
qint8 nodeType;
QUuid nodeUUID, connectionUUID;
HifiSockAddr nodePublicSocket, nodeLocalSocket;
bool canAdjustLocks;
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> canAdjustLocks;
// 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
@ -396,7 +399,7 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
nodePublicSocket.setAddress(_domainHandler.getIP()); nodePublicSocket.setAddress(_domainHandler.getIP());
} }
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket); SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket, canAdjustLocks);
packetStream >> connectionUUID; packetStream >> connectionUUID;
node->setConnectionSecret(connectionUUID); node->setConnectionSecret(connectionUUID);

View file

@ -64,7 +64,7 @@ PacketVersion versionForPacketType(PacketType type) {
return 2; return 2;
case PacketTypeDomainList: case PacketTypeDomainList:
case PacketTypeDomainListRequest: case PacketTypeDomainListRequest:
return 3; return 4;
case PacketTypeCreateAssignment: case PacketTypeCreateAssignment:
case PacketTypeRequestAssignment: case PacketTypeRequestAssignment:
return 2; return 2;

View file

@ -107,7 +107,7 @@ void EntityTests::entityTreeTests(bool verbose) {
properties.setPosition(newPosition); properties.setPosition(newPosition);
tree.updateEntity(entityID, properties); tree.updateEntity(entityID, properties, true);
float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionNearOriginInTreeUnits, targetRadius); const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionNearOriginInTreeUnits, targetRadius);
@ -147,7 +147,7 @@ void EntityTests::entityTreeTests(bool verbose) {
properties.setPosition(newPosition); properties.setPosition(newPosition);
tree.updateEntity(entityID, properties); tree.updateEntity(entityID, properties, true);
float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenterInTreeUnits, targetRadius); const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenterInTreeUnits, targetRadius);