collect permissions into their own data structure

This commit is contained in:
Seth Alves 2016-06-02 13:15:30 -07:00
parent e24ba4caf6
commit 83f2c723eb
15 changed files with 361 additions and 339 deletions

View file

@ -286,8 +286,8 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<ReceivedMes
if (!senderID.isNull()) { if (!senderID.isNull()) {
// We don't have this node yet - we should add it // We don't have this node yet - we should add it
matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode(senderID, NodeType::Unassigned,
(senderID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false, false); senderSockAddr, senderSockAddr);
auto childData = std::unique_ptr<AssignmentClientChildData> auto childData = std::unique_ptr<AssignmentClientChildData>
{ new AssignmentClientChildData(Assignment::Type::AllTypes) }; { new AssignmentClientChildData(Assignment::Type::AllTypes) };

View file

@ -135,6 +135,12 @@
"label": "Write Assets", "label": "Write Assets",
"type": "checkbox", "type": "checkbox",
"default": false "default": false
},
{
"name": "id_can_connect_past_max_capacity",
"label": "Ignore Max Capacity",
"type": "checkbox",
"default": false
} }
] ]
} }

View file

@ -1,40 +0,0 @@
//
// AgentPermissions.h
// domain-server/src
//
// Created by Seth Alves on 2016-6-1.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AgentPermissions_h
#define hifi_AgentPermissions_h
class AgentPermissions {
public:
AgentPermissions(QMap<QString, QVariant> perms) {
_id = perms["permissions_id"].toString();
canConnectToDomain = perms["id_can_connect"].toBool();
canAdjustLocks = perms["id_can_adjust_locks"].toBool();
canRezPermanentEntities = perms["id_can_rez"].toBool();
canRezTemporaryEntities = perms["id_can_rez_tmp"].toBool();
canWriteToAssetServer = perms["id_can_write_to_asset_server"].toBool();
};
QString getID() { return _id; }
bool canConnectToDomain { false };
bool canAdjustLocks { false };
bool canRezPermanentEntities { false };
bool canRezTemporaryEntities { false };
bool canWriteToAssetServer { false };
protected:
QString _id;
};
using AgentPermissionsPointer = std::shared_ptr<AgentPermissions>;
#endif // hifi_AgentPermissions_h

View file

@ -165,15 +165,16 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
_pendingAssignedNodes.erase(it); _pendingAssignedNodes.erase(it);
// always allow assignment clients to create and destroy entities // always allow assignment clients to create and destroy entities
newNode->setIsAllowedEditor(true); AgentPermissions userPerms;
newNode->setCanRez(true); userPerms.canAdjustLocks = true;
userPerms.canRezPermanentEntities = true;
newNode->setPermissions(userPerms);
return newNode; return newNode;
} }
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity"; const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors"; // const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers"; // const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers";
SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection, SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection,
const QString& username, const QString& username,
@ -181,92 +182,52 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
auto limitedNodeList = DependencyManager::get<LimitedNodeList>(); auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
bool isRestrictingAccess = // start with empty permissions
_server->_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); AgentPermissions userPerms(username);
userPerms.setAll(false);
// check if this user is on our local machine - if this is true they are always allowed to connect // check if this user is on our local machine - if this is true they are always allowed to connect
QHostAddress senderHostAddress = nodeConnection.senderSockAddr.getAddress(); QHostAddress senderHostAddress = nodeConnection.senderSockAddr.getAddress();
bool isLocalUser = bool isLocalUser =
(senderHostAddress == limitedNodeList->getLocalSockAddr().getAddress() || senderHostAddress == QHostAddress::LocalHost); (senderHostAddress == limitedNodeList->getLocalSockAddr().getAddress() || senderHostAddress == QHostAddress::LocalHost);
if (isLocalUser) {
// if we're using restricted access and this user is not local make sure we got a user signature userPerms |= _server->_settingsManager.getPermissionsForName("localhost");
if (isRestrictingAccess && !isLocalUser) {
if (!username.isEmpty()) {
if (usernameSignature.isEmpty()) {
// if user didn't include usernameSignature in connect request, send a connectionToken packet
sendConnectionTokenPacket(username, nodeConnection.senderSockAddr);
// ask for their public key right now to make sure we have it
requestUserPublicKey(username);
return SharedNodePointer();
}
}
} }
bool verifiedUsername = false; if (!username.isEmpty() && usernameSignature.isEmpty()) {
// user is attempting to prove their identity to us, but we don't have enough information
// if we do not have a local user we need to subject them to our verification and capacity checks sendConnectionTokenPacket(username, nodeConnection.senderSockAddr);
if (!isLocalUser) { // ask for their public key right now to make sure we have it
requestUserPublicKey(username);
// check if we need to look at the username signature if (!isLocalUser) {
if (isRestrictingAccess) {
if (isVerifiedAllowedUser(username, usernameSignature, nodeConnection.senderSockAddr)) {
// we verified the user via their username and signature - set the verifiedUsername
// so we don't re-decrypt their sig if we're trying to exempt them from max capacity check (due to
// being in the allowed editors list)
verifiedUsername = true;
} else {
// failed to verify user - return a null shared ptr
return SharedNodePointer();
}
}
if (!isWithinMaxCapacity(username, usernameSignature, verifiedUsername, nodeConnection.senderSockAddr)) {
// we can't allow this user to connect because we are at max capacity (and they either aren't an allowed editor
// or couldn't be verified as one)
return SharedNodePointer(); return SharedNodePointer();
} }
} }
// if this user is in the editors list (or if the editors list is empty) set the user's node's isAllowedEditor to true if (username.isEmpty()) {
const QVariant* allowedEditorsVariant = // they didn't tell us who they are
valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH); userPerms |= _server->_settingsManager.getPermissionsForName("anonymous");
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList(); } else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) {
// they are sent us a username and the signature verifies it
// if the allowed editors list is empty then everyone can adjust locks userPerms |= _server->_settingsManager.getPermissionsForName(username);
bool isAllowedEditor = allowedEditors.empty(); userPerms |= _server->_settingsManager.getPermissionsForName("logged-in");
} else {
if (allowedEditors.contains(username, Qt::CaseInsensitive)) { // they sent us a username, but it didn't check out
// we have a non-empty allowed editors list - check if this user is verified to be in it requestUserPublicKey(username);
if (!verifiedUsername) { if (!isLocalUser) {
if (!verifyUserSignature(username, usernameSignature, HifiSockAddr())) { return SharedNodePointer();
// failed to verify a user that is in the allowed editors list
// TODO: fix public key refresh in interface/metaverse and force this check
qDebug() << "Could not verify user" << username << "as allowed editor. In the interim this user"
<< "will be given edit rights to avoid a thrasing of public key requests and connect requests.";
}
isAllowedEditor = true;
} else {
// already verified this user and they are in the allowed editors list
isAllowedEditor = true;
} }
} }
// check if only editors should be able to rez entities if (!userPerms.canConnectToDomain) {
const QVariant* editorsAreRezzersVariant = return SharedNodePointer();
valueForKeyPath(_server->_settingsManager.getSettingsMap(), EDITORS_ARE_REZZERS_KEYPATH);
bool onlyEditorsAreRezzers = false;
if (editorsAreRezzersVariant) {
onlyEditorsAreRezzers = editorsAreRezzersVariant->toBool();
} }
bool canRez = true; if (!userPerms.canConnectPastMaxCapacity && !isWithinMaxCapacity()) {
if (onlyEditorsAreRezzers) { // we can't allow this user to connect because we are at max capacity
canRez = isAllowedEditor; sendConnectionDeniedPacket("Too many connected users.", nodeConnection.senderSockAddr,
DomainHandler::ConnectionRefusedReason::TooManyUsers);
return SharedNodePointer();
} }
QUuid hintNodeID; QUuid hintNodeID;
@ -290,8 +251,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID); SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID);
// set the edit rights for this user // set the edit rights for this user
newNode->setIsAllowedEditor(isAllowedEditor); newNode->setPermissions(userPerms);
newNode->setCanRez(canRez);
// grab the linked data for our new node so we can set the username // grab the linked data for our new node so we can set the username
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData()); DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
@ -407,67 +367,17 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
return false; return false;
} }
bool DomainGatekeeper::isVerifiedAllowedUser(const QString& username, const QByteArray& usernameSignature, bool DomainGatekeeper::isWithinMaxCapacity() {
const HifiSockAddr& senderSockAddr) {
if (username.isEmpty()) {
qDebug() << "Connect request denied - no username provided.";
sendConnectionDeniedPacket("No username provided", senderSockAddr,
DomainHandler::ConnectionRefusedReason::LoginError);
return false;
}
QStringList allowedUsers =
_server->_settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList();
if (allowedUsers.contains(username, Qt::CaseInsensitive)) {
if (!verifyUserSignature(username, usernameSignature, senderSockAddr)) {
return false;
}
} else {
qDebug() << "Connect request denied for user" << username << "- not in allowed users list.";
sendConnectionDeniedPacket("User not on whitelist.", senderSockAddr,
DomainHandler::ConnectionRefusedReason::NotAuthorized);
return false;
}
return true;
}
bool DomainGatekeeper::isWithinMaxCapacity(const QString& username, const QByteArray& usernameSignature,
bool& verifiedUsername,
const HifiSockAddr& senderSockAddr) {
// find out what our maximum capacity is // find out what our maximum capacity is
const QVariant* maximumUserCapacityVariant = valueForKeyPath(_server->_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY); const QVariant* maximumUserCapacityVariant =
valueForKeyPath(_server->_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY);
unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0; unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0;
if (maximumUserCapacity > 0) { if (maximumUserCapacity > 0) {
unsigned int connectedUsers = _server->countConnectedUsers(); unsigned int connectedUsers = _server->countConnectedUsers();
if (connectedUsers >= maximumUserCapacity) { if (connectedUsers >= maximumUserCapacity) {
// too many users, deny the new connection unless this user is an allowed editor
const QVariant* allowedEditorsVariant =
valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
if (allowedEditors.contains(username)) {
if (verifiedUsername || verifyUserSignature(username, usernameSignature, senderSockAddr)) {
verifiedUsername = true;
qDebug() << "Above maximum capacity -" << connectedUsers << "/" << maximumUserCapacity <<
"but user" << username << "is in allowed editors list so will be allowed to connect.";
return true;
}
}
// deny connection from this user
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection."; qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection.";
sendConnectionDeniedPacket("Too many connected users.", senderSockAddr,
DomainHandler::ConnectionRefusedReason::TooManyUsers);
return false; return false;
} }
@ -479,8 +389,7 @@ bool DomainGatekeeper::isWithinMaxCapacity(const QString& username, const QByteA
void DomainGatekeeper::preloadAllowedUserPublicKeys() { void DomainGatekeeper::preloadAllowedUserPublicKeys() {
const QVariant* allowedUsersVariant = valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH); QStringList allowedUsers = _server->_settingsManager.getAllNames();
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
if (allowedUsers.size() > 0) { if (allowedUsers.size() > 0) {
// in the future we may need to limit how many requests here - for now assume that lists of allowed users are not // in the future we may need to limit how many requests here - for now assume that lists of allowed users are not

View file

@ -66,11 +66,7 @@ private:
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature, bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr); const HifiSockAddr& senderSockAddr);
bool isVerifiedAllowedUser(const QString& username, const QByteArray& usernameSignature, bool isWithinMaxCapacity();
const HifiSockAddr& senderSockAddr);
bool isWithinMaxCapacity(const QString& username, const QByteArray& usernameSignature,
bool& verifiedUsername,
const HifiSockAddr& senderSockAddr);
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr); const HifiSockAddr& senderSockAddr);

View file

@ -1084,8 +1084,7 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
// add a flag to indicate if this domain uses restricted access - for now that will exclude it from listings // add a flag to indicate if this domain uses restricted access - for now that will exclude it from listings
const QString RESTRICTED_ACCESS_FLAG = "restricted"; const QString RESTRICTED_ACCESS_FLAG = "restricted";
domainObject[RESTRICTED_ACCESS_FLAG] = domainObject[RESTRICTED_ACCESS_FLAG] = _settingsManager.getAllNames().length() > 0;
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
// figure out the breakdown of currently connected interface clients // figure out the breakdown of currently connected interface clients
int numConnectedUnassigned = 0; int numConnectedUnassigned = 0;

View file

@ -110,6 +110,8 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
// This was prior to the introduction of security.restricted_access // This was prior to the introduction of security.restricted_access
// If the user has a list of allowed users then set their value for security.restricted_access to true // If the user has a list of allowed users then set their value for security.restricted_access to true
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access";
QVariant* allowedUsers = valueForKeyPath(_configMap.getMergedConfig(), ALLOWED_USERS_SETTINGS_KEYPATH); QVariant* allowedUsers = valueForKeyPath(_configMap.getMergedConfig(), ALLOWED_USERS_SETTINGS_KEYPATH);
if (allowedUsers if (allowedUsers
@ -207,10 +209,47 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
} }
void DomainServerSettingsManager::unpackPermissions() { void DomainServerSettingsManager::unpackPermissions() {
QList<QVariant> permsHashList = valueOrDefaultValueForKeyPath(AGENT_PERMISSIONS_KEYPATH).toList(); bool foundLocalhost = false;
foreach (QVariant permsHash, permsHashList) { bool foundAnonymous = false;
bool foundLoggedIn = false;
// XXX check for duplicate IDs
QVariant* permissions = valueForKeyPath(_configMap.getMergedConfig(), AGENT_PERMISSIONS_KEYPATH);
if (!permissions->canConvert(QMetaType::QVariantList)) {
qDebug() << "failed to extract permissions from settings.";
return;
}
// QList<QVariant> permissionsList = permissions->toList();
QVariantList* permissionsList = reinterpret_cast<QVariantList*>(permissions);
foreach (QVariant permsHash, *permissionsList) {
AgentPermissionsPointer perms { new AgentPermissions(permsHash.toMap()) }; AgentPermissionsPointer perms { new AgentPermissions(permsHash.toMap()) };
_agentPermissions[perms->getID()] = perms; QString id = perms->getID();
foundLoggedIn |= (id == "localhost");
foundAnonymous |= (id == "anonymous");
foundLoggedIn |= (id == "logged-in");
_agentPermissions[id] = perms;
}
// if any of the standard names are missing, add them
if (!foundLocalhost) {
AgentPermissionsPointer perms { new AgentPermissions("localhost") };
perms->setAll(true);
_agentPermissions["localhost"] = perms;
*permissionsList += perms->toVariant();
}
if (!foundAnonymous) {
AgentPermissionsPointer perms { new AgentPermissions("anonymous") };
_agentPermissions["anonymous"] = perms;
*permissionsList += perms->toVariant();
}
if (!foundLoggedIn) {
AgentPermissionsPointer perms { new AgentPermissions("logged-in") };
_agentPermissions["logged-in"] = perms;
*permissionsList += perms->toVariant();
} }
#ifdef WANT_DEBUG #ifdef WANT_DEBUG
@ -229,11 +268,13 @@ void DomainServerSettingsManager::unpackPermissions() {
#endif #endif
} }
AgentPermissionsPointer DomainServerSettingsManager::getPermissionsForName(QString name) const { AgentPermissions DomainServerSettingsManager::getPermissionsForName(const QString& name) const {
if (_agentPermissions.contains(name)) { if (_agentPermissions.contains(name)) {
return _agentPermissions[name]; return *(_agentPermissions[name].get());
} }
return nullptr; AgentPermissions nullPermissions;
nullPermissions.setAll(false);
return nullPermissions;
} }
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) {

View file

@ -25,9 +25,6 @@ const QString SETTINGS_PATHS_KEY = "paths";
const QString SETTINGS_PATH = "/settings"; const QString SETTINGS_PATH = "/settings";
const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json"; const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json";
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access";
const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions"; const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions";
class DomainServerSettingsManager : public QObject { class DomainServerSettingsManager : public QObject {
@ -43,7 +40,8 @@ public:
QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); } QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); }
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); } QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
AgentPermissionsPointer getPermissionsForName(QString name) const; AgentPermissions getPermissionsForName(const QString& name) const;
QStringList getAllNames() { return _agentPermissions.keys(); }
private slots: private slots:
void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message); void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message);

View file

@ -0,0 +1,43 @@
//
// AgentPermissions.cpp
// libraries/networking/src/
//
// Created by Seth Alves on 2016-6-1.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDataStream>
#include "AgentPermissions.h"
AgentPermissions& AgentPermissions::operator|=(const AgentPermissions& rhs) {
this->canConnectToDomain |= rhs.canConnectToDomain;
this->canAdjustLocks |= rhs.canAdjustLocks;
this->canRezPermanentEntities |= rhs.canRezPermanentEntities;
this->canRezTemporaryEntities |= rhs.canRezTemporaryEntities;
this->canWriteToAssetServer |= rhs.canWriteToAssetServer;
this->canConnectPastMaxCapacity |= rhs.canConnectPastMaxCapacity;
return *this;
}
QDataStream& operator<<(QDataStream& out, const AgentPermissions& perms) {
out << perms.canConnectToDomain;
out << perms.canAdjustLocks;
out << perms.canRezPermanentEntities;
out << perms.canRezTemporaryEntities;
out << perms.canWriteToAssetServer;
out << perms.canConnectPastMaxCapacity;
return out;
}
QDataStream& operator>>(QDataStream& in, AgentPermissions& perms) {
in >> perms.canConnectToDomain;
in >> perms.canAdjustLocks;
in >> perms.canRezPermanentEntities;
in >> perms.canRezTemporaryEntities;
in >> perms.canWriteToAssetServer;
in >> perms.canConnectPastMaxCapacity;
return in;
}

View file

@ -0,0 +1,79 @@
//
// AgentPermissions.h
// libraries/networking/src/
//
// Created by Seth Alves on 2016-6-1.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AgentPermissions_h
#define hifi_AgentPermissions_h
#include <memory>
#include <QString>
#include <QMap>
#include <QVariant>
#include <QUuid>
class AgentPermissions;
using AgentPermissionsPointer = std::shared_ptr<AgentPermissions>;
class AgentPermissions {
public:
AgentPermissions() { _id = QUuid::createUuid().toString(); }
AgentPermissions(const QString& name) { _id = name; }
AgentPermissions(QMap<QString, QVariant> perms) {
_id = perms["permissions_id"].toString();
canConnectToDomain = perms["id_can_connect"].toBool();
canAdjustLocks = perms["id_can_adjust_locks"].toBool();
canRezPermanentEntities = perms["id_can_rez"].toBool();
canRezTemporaryEntities = perms["id_can_rez_tmp"].toBool();
canWriteToAssetServer = perms["id_can_write_to_asset_server"].toBool();
canConnectPastMaxCapacity = perms["id_can_connect_past_max_capacity"].toBool();
}
QString getID() { return _id; }
// the initializations here should match the defaults in describe-settings.json
bool canConnectToDomain { true };
bool canAdjustLocks { false };
bool canRezPermanentEntities { false };
bool canRezTemporaryEntities { false };
bool canWriteToAssetServer { false };
bool canConnectPastMaxCapacity { false };
void setAll(bool value) {
canConnectToDomain = value;
canAdjustLocks = value;
canRezPermanentEntities = value;
canRezTemporaryEntities = value;
canWriteToAssetServer = value;
canConnectPastMaxCapacity = value;
}
QVariant toVariant() {
QMap<QString, QVariant> values;
values["permissions_id"] = _id;
values["id_can_connect"] = canConnectToDomain;
values["id_can_adjust_locks"] = canAdjustLocks;
values["id_can_rez"] = canRezPermanentEntities;
values["id_can_rez_tmp"] = canRezTemporaryEntities;
values["id_can_write_to_asset_server"] = canWriteToAssetServer;
values["id_can_connect_past_max_capacity"] = canConnectPastMaxCapacity;
return QVariant(values);
}
AgentPermissions& operator|=(const AgentPermissions& rhs);
friend QDataStream& operator<<(QDataStream& out, const AgentPermissions& perms);
friend QDataStream& operator>>(QDataStream& in, AgentPermissions& perms);
protected:
QString _id;
};
const AgentPermissions DEFAULT_AGENT_PERMISSIONS;
#endif // hifi_AgentPermissions_h

View file

@ -52,7 +52,7 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
_numCollectedPackets(0), _numCollectedPackets(0),
_numCollectedBytes(0), _numCollectedBytes(0),
_packetStatTimer(), _packetStatTimer(),
_thisNodeCanRez(true) _permissions(AgentPermissions())
{ {
static bool firstCall = true; static bool firstCall = true;
if (firstCall) { if (firstCall) {
@ -130,17 +130,25 @@ void LimitedNodeList::setSessionUUID(const QUuid& sessionUUID) {
} }
} }
void LimitedNodeList::setIsAllowedEditor(bool isAllowedEditor) {
if (_isAllowedEditor != isAllowedEditor) {
_isAllowedEditor = isAllowedEditor;
emit isAllowedEditorChanged(isAllowedEditor);
}
}
void LimitedNodeList::setThisNodeCanRez(bool canRez) { void LimitedNodeList::setPermissions(const AgentPermissions& newPermissions) {
if (_thisNodeCanRez != canRez) { bool emitIsAllowedEditorChanged { false };
_thisNodeCanRez = canRez; bool emitCanRezChanged { false };
emit canRezChanged(canRez);
if (_permissions.canAdjustLocks != newPermissions.canAdjustLocks) {
emitIsAllowedEditorChanged = true;
}
if (_permissions.canRezPermanentEntities != newPermissions.canRezPermanentEntities) {
emitCanRezChanged = true;
}
_permissions = newPermissions;
if (emitIsAllowedEditorChanged) {
emit isAllowedEditorChanged(_permissions.canAdjustLocks);
}
if (emitCanRezChanged) {
emit canRezChanged(_permissions.canRezPermanentEntities);
} }
} }
@ -515,7 +523,7 @@ void LimitedNodeList::handleNodeKill(const SharedNodePointer& node) {
SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType, SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
bool isAllowedEditor, bool canRez, const AgentPermissions& permissions,
const QUuid& connectionSecret) { const QUuid& connectionSecret) {
NodeHash::const_iterator it = _nodeHash.find(uuid); NodeHash::const_iterator it = _nodeHash.find(uuid);
@ -524,14 +532,13 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
matchingNode->setPublicSocket(publicSocket); matchingNode->setPublicSocket(publicSocket);
matchingNode->setLocalSocket(localSocket); matchingNode->setLocalSocket(localSocket);
matchingNode->setIsAllowedEditor(isAllowedEditor); matchingNode->setPermissions(permissions);
matchingNode->setCanRez(canRez);
matchingNode->setConnectionSecret(connectionSecret); matchingNode->setConnectionSecret(connectionSecret);
return matchingNode; return matchingNode;
} else { } else {
// we didn't have this node, so add them // we didn't have this node, so add them
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, isAllowedEditor, canRez, connectionSecret, this); Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, permissions, connectionSecret, this);
if (nodeType == NodeType::AudioMixer) { if (nodeType == NodeType::AudioMixer) {
LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer); LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer);

View file

@ -104,11 +104,9 @@ public:
const QUuid& getSessionUUID() const { return _sessionUUID; } const QUuid& getSessionUUID() const { return _sessionUUID; }
void setSessionUUID(const QUuid& sessionUUID); void setSessionUUID(const QUuid& sessionUUID);
bool isAllowedEditor() const { return _isAllowedEditor; } void setPermissions(const AgentPermissions& newPermissions);
void setIsAllowedEditor(bool isAllowedEditor); bool isAllowedEditor() const { return _permissions.canAdjustLocks; }
bool getThisNodeCanRez() const { return _permissions.canRezPermanentEntities; }
bool getThisNodeCanRez() const { return _thisNodeCanRez; }
void setThisNodeCanRez(bool canRez);
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); } quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
QUdpSocket& getDTLSSocket(); QUdpSocket& getDTLSSocket();
@ -137,7 +135,7 @@ public:
SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType, SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
bool isAllowedEditor = false, bool canRez = false, const AgentPermissions& permissions = DEFAULT_AGENT_PERMISSIONS,
const QUuid& connectionSecret = QUuid()); const QUuid& connectionSecret = QUuid());
bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; } bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; }
@ -300,8 +298,7 @@ protected:
int _numCollectedBytes; int _numCollectedBytes;
QElapsedTimer _packetStatTimer; QElapsedTimer _packetStatTimer;
bool _isAllowedEditor { false }; AgentPermissions _permissions;
bool _thisNodeCanRez;
QPointer<QTimer> _initialSTUNTimer; QPointer<QTimer> _initialSTUNTimer;

View file

@ -16,6 +16,7 @@
#include "Node.h" #include "Node.h"
#include "SharedUtil.h" #include "SharedUtil.h"
#include "AgentPermissions.h"
#include <QtCore/QDataStream> #include <QtCore/QDataStream>
#include <QtCore/QDebug> #include <QtCore/QDebug>
@ -47,7 +48,7 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
} }
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
const HifiSockAddr& localSocket, bool isAllowedEditor, bool canRez, const QUuid& connectionSecret, const HifiSockAddr& localSocket, const AgentPermissions& permissions, const QUuid& connectionSecret,
QObject* parent) : QObject* parent) :
NetworkPeer(uuid, publicSocket, localSocket, parent), NetworkPeer(uuid, publicSocket, localSocket, parent),
_type(type), _type(type),
@ -57,8 +58,7 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
_clockSkewUsec(0), _clockSkewUsec(0),
_mutex(), _mutex(),
_clockSkewMovingPercentile(30, 0.8f), // moving 80th percentile of 30 samples _clockSkewMovingPercentile(30, 0.8f), // moving 80th percentile of 30 samples
_isAllowedEditor(isAllowedEditor), _permissions(permissions)
_canRez(canRez)
{ {
// Update socket's object name // Update socket's object name
setType(_type); setType(_type);
@ -78,15 +78,12 @@ void Node::updateClockSkewUsec(qint64 clockSkewSample) {
_clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile(); _clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
} }
QDataStream& operator<<(QDataStream& out, const Node& node) { QDataStream& operator<<(QDataStream& out, const Node& node) {
out << node._type; out << node._type;
out << node._uuid; out << node._uuid;
out << node._publicSocket; out << node._publicSocket;
out << node._localSocket; out << node._localSocket;
out << node._isAllowedEditor; out << node._permissions;
out << node._canRez;
return out; return out;
} }
@ -95,9 +92,7 @@ QDataStream& operator>>(QDataStream& in, Node& node) {
in >> node._uuid; in >> node._uuid;
in >> node._publicSocket; in >> node._publicSocket;
in >> node._localSocket; in >> node._localSocket;
in >> node._isAllowedEditor; in >> node._permissions;
in >> node._canRez;
return in; return in;
} }

View file

@ -27,13 +27,14 @@
#include "NodeType.h" #include "NodeType.h"
#include "SimpleMovingAverage.h" #include "SimpleMovingAverage.h"
#include "MovingPercentile.h" #include "MovingPercentile.h"
#include "AgentPermissions.h"
class Node : public NetworkPeer { class Node : public NetworkPeer {
Q_OBJECT Q_OBJECT
public: public:
Node(const QUuid& uuid, NodeType_t type, Node(const QUuid& uuid, NodeType_t type,
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
bool isAllowedEditor, bool canRez, const QUuid& connectionSecret = QUuid(), const AgentPermissions& permissions, const QUuid& connectionSecret = QUuid(),
QObject* parent = 0); QObject* parent = 0);
bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; } bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; }
@ -58,11 +59,10 @@ public:
void updateClockSkewUsec(qint64 clockSkewSample); void updateClockSkewUsec(qint64 clockSkewSample);
QMutex& getMutex() { return _mutex; } QMutex& getMutex() { return _mutex; }
void setIsAllowedEditor(bool isAllowedEditor) { _isAllowedEditor = isAllowedEditor; } void setPermissions(const AgentPermissions& newPermissions) { _permissions = newPermissions; }
bool isAllowedEditor() { return _isAllowedEditor; } AgentPermissions getPermissions() const { return _permissions; }
bool isAllowedEditor() const { return _permissions.canAdjustLocks; }
void setCanRez(bool canRez) { _canRez = canRez; } bool getCanRez() const { return _permissions.canRezPermanentEntities; }
bool getCanRez() { return _canRez; }
friend QDataStream& operator<<(QDataStream& out, const Node& node); friend QDataStream& operator<<(QDataStream& out, const Node& node);
friend QDataStream& operator>>(QDataStream& in, Node& node); friend QDataStream& operator>>(QDataStream& in, Node& node);
@ -81,8 +81,7 @@ private:
qint64 _clockSkewUsec; qint64 _clockSkewUsec;
QMutex _mutex; QMutex _mutex;
MovingPercentile _clockSkewMovingPercentile; MovingPercentile _clockSkewMovingPercentile;
bool _isAllowedEditor; AgentPermissions _permissions;
bool _canRez;
}; };
Q_DECLARE_METATYPE(Node*) Q_DECLARE_METATYPE(Node*)

View file

@ -543,13 +543,8 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
packetStream >> newUUID; packetStream >> newUUID;
setSessionUUID(newUUID); setSessionUUID(newUUID);
quint8 isAllowedEditor; // pull the permissions/right/privileges for this node out of the stream
packetStream >> isAllowedEditor; packetStream >> _permissions;
setIsAllowedEditor((bool) isAllowedEditor);
quint8 thisNodeCanRez;
packetStream >> thisNodeCanRez;
setThisNodeCanRez((bool) thisNodeCanRez);
// pull each node in the packet // pull each node in the packet
while (packetStream.device()->pos() < message->getSize()) { while (packetStream.device()->pos() < message->getSize()) {
@ -577,10 +572,9 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
qint8 nodeType; qint8 nodeType;
QUuid nodeUUID, connectionUUID; QUuid nodeUUID, connectionUUID;
HifiSockAddr nodePublicSocket, nodeLocalSocket; HifiSockAddr nodePublicSocket, nodeLocalSocket;
bool isAllowedEditor; AgentPermissions permissions;
bool canRez;
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> isAllowedEditor >> canRez; packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> permissions;
// if the public socket address is 0 then it's reachable at the same IP // if the public socket address is 0 then it's reachable at the same IP
// as the domain server // as the domain server
@ -591,8 +585,7 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
packetStream >> connectionUUID; packetStream >> connectionUUID;
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
nodeLocalSocket, isAllowedEditor, canRez, nodeLocalSocket, permissions, connectionUUID);
connectionUUID);
} }
void NodeList::sendAssignment(Assignment& assignment) { void NodeList::sendAssignment(Assignment& assignment) {