From 22ca34590a67356fcabb4938bbb01d972b860be9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Jun 2016 16:39:47 -0700 Subject: [PATCH 01/98] add domain-server settings-page UI for groups --- .../resources/describe-settings.json | 75 ++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index bad24dd3a1..a12e83df78 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -176,7 +176,7 @@ "groups": [ { - "label": "User / Group", + "label": "Type of User", "span": 1 }, { @@ -245,7 +245,7 @@ "groups": [ { - "label": "User / Group", + "label": "User", "span": 1 }, { @@ -302,6 +302,77 @@ "default": false } ] + }, + { + "name": "group_permissions", + "type": "table", + "caption": "Permissions for Users in Groups", + "can_add_new_rows": true, + + "groups": [ + { + "label": "Group", + "span": 2 + }, + { + "label": "Permissions ?", + "span": 6 + } + ], + + "columns": [ + { + "name": "permissions_id", + "label": "Group Name" + }, + { + "name": "group_id", + "label": "Group ID", + "readonly": true + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": true + }, + { + "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 + } + ] } ] }, From 3072a678393b9f148b4a8aa16b9666d44c19e5f0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Jun 2016 16:40:07 -0700 Subject: [PATCH 02/98] center help test above group-permissions table --- domain-server/resources/web/css/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index 2862feed87..b9fc8b16e7 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -133,7 +133,7 @@ table .headers + .headers td { color: #222; } -table[name="security.standard_permissions"] .headers td + td, table[name="security.permissions"] .headers td + td { +table[name="security.standard_permissions"] .headers td + td, table[name="security.permissions"] .headers td + td, table[name="security.group_permissions"] .headers td + td { text-align: center; } From 52e3be4f5d4265f1ab29ca527e13368b8949096c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Jun 2016 16:40:36 -0700 Subject: [PATCH 03/98] allow for read-only text-fields in table input rows --- domain-server/resources/web/settings/js/settings.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index aecc48b31f..48312e0e84 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1,6 +1,7 @@ var Settings = { showAdvanced: false, - METAVERSE_URL: 'https://metaverse.highfidelity.com', + // METAVERSE_URL: 'https://metaverse.highfidelity.com', + METAVERSE_URL: 'http://localhost:3000', ADVANCED_CLASS: 'advanced-setting', TRIGGER_CHANGE_CLASS: 'trigger-change', DATA_ROW_CLASS: 'value-row', @@ -1065,7 +1066,7 @@ function makeTableInputs(setting) { } else { html += "\ \ + value='" + (col.default ? col.default : "") + "' data-default='" + (col.default ? col.default : "") + "'" + (col.readonly ? " readonly" : "") + ">\ " } }) From d184aade1b41407034a3a849306f871eaf01d55c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Jun 2016 16:40:57 -0700 Subject: [PATCH 04/98] comments --- domain-server/src/DomainGatekeeper.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index c4a7d1a425..74269a5794 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -262,8 +262,14 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect qDebug() << "user-permissions: no username, so:" << userPerms; } else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { // they are sent us a username and the signature verifies it + + + // TODO check groups here + + if (_server->_settingsManager.havePermissionsForName(username)) { - // we have specific permissions for this user. + // we have specific permissions for this user. Discard any other permissions and set this + // agent's permissions to be exactly what's indicated on their row of the permissions-grid. userPerms = _server->_settingsManager.getPermissionsForName(username); qDebug() << "user-permissions: specific user matches, so:" << userPerms; } else { From dd24143d16c05ca9118a61e5f7c457b50212aa42 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Jun 2016 16:41:47 -0700 Subject: [PATCH 05/98] start on handling group permissions --- .../src/DomainServerSettingsManager.cpp | 96 ++++++++++++++++++- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 5790eb9178..af0d5afab6 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -269,19 +270,21 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList void DomainServerSettingsManager::packPermissionsForMap(QString mapName, QHash agentPermissions, QString keyPath) { + // find (or create) the "security" section of the settings map QVariant* security = valueForKeyPath(_configMap.getUserConfig(), "security"); if (!security || !security->canConvert(QMetaType::QVariantMap)) { security = valueForKeyPath(_configMap.getUserConfig(), "security", true); (*security) = QVariantMap(); } - // save settings for anonymous / logged-in / localhost + // find (or create) whichever subsection of "security" we are packing QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath); if (!permissions || !permissions->canConvert(QMetaType::QVariantList)) { permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath, true); (*permissions) = QVariantList(); } + // convert details for each member of the section QVariantList* permissionsList = reinterpret_cast(permissions); (*permissionsList).clear(); foreach (QString userName, agentPermissions.keys()) { @@ -291,11 +294,16 @@ void DomainServerSettingsManager::packPermissionsForMap(QString mapName, void DomainServerSettingsManager::packPermissions() { // transfer details from _agentPermissions to _configMap + + // save settings for anonymous / logged-in / localhost packPermissionsForMap("standard_permissions", _standardAgentPermissions, AGENT_STANDARD_PERMISSIONS_KEYPATH); // save settings for specific users packPermissionsForMap("permissions", _agentPermissions, AGENT_PERMISSIONS_KEYPATH); + // save settings for groups + packPermissionsForMap("permissions", _groupPermissions, AGENT_PERMISSIONS_KEYPATH); + persistToFile(); _configMap.loadMasterAndUserConfig(_argumentList); } @@ -305,6 +313,7 @@ void DomainServerSettingsManager::unpackPermissions() { _standardAgentPermissions.clear(); _agentPermissions.clear(); + _groupPermissions.clear(); bool foundLocalhost = false; bool foundAnonymous = false; @@ -323,6 +332,12 @@ void DomainServerSettingsManager::unpackPermissions() { permissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_PERMISSIONS_KEYPATH, true); (*permissions) = QVariantList(); } + QVariant* groupPermissions = valueForKeyPath(_configMap.getUserConfig(), GROUP_PERMISSIONS_KEYPATH); + if (!groupPermissions || !groupPermissions->canConvert(QMetaType::QVariantList)) { + qDebug() << "failed to extract group permissions from settings."; + groupPermissions = valueForKeyPath(_configMap.getUserConfig(), GROUP_PERMISSIONS_KEYPATH, true); + (*groupPermissions) = QVariantList(); + } QList standardPermissionsList = standardPermissions->toList(); foreach (QVariant permsHash, standardPermissionsList) { @@ -353,6 +368,19 @@ void DomainServerSettingsManager::unpackPermissions() { } } + QList groupPermissionsList = groupPermissions->toList(); + foreach (QVariant permsHash, groupPermissionsList) { + NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; + QString id = perms->getID(); + if (_groupPermissions.contains(id)) { + qDebug() << "duplicate name in group permissions table: " << id; + _groupPermissions[id] |= perms; + needPack = true; + } else { + _groupPermissions[id] = perms; + } + } + // if any of the standard names are missing, add them if (!foundLocalhost) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLocalhost) }; @@ -375,16 +403,24 @@ void DomainServerSettingsManager::unpackPermissions() { packPermissions(); } + // attempt to retrieve any missing group-IDs + requestMissingGroupIDs(); + + #ifdef WANT_DEBUG qDebug() << "--------------- permissions ---------------------"; QList> permissionsSets; - permissionsSets << _standardAgentPermissions << _agentPermissions; + permissionsSets << _standardAgentPermissions << _agentPermissions << _groupPermissions; foreach (auto permissionSet, permissionsSets) { QHashIterator i(permissionSet); while (i.hasNext()) { i.next(); NodePermissionsPointer perms = i.value(); - qDebug() << i.key() << perms; + if (perms->isGroup()) { + qDebug() << i.key() << perms->getGroupID() << perms; + } else { + qDebug() << i.key() << perms; + } } } #endif @@ -827,3 +863,57 @@ void DomainServerSettingsManager::persistToFile() { qCritical("Could not write to JSON settings file. Unable to persist settings."); } } + +void DomainServerSettingsManager::requestMissingGroupIDs() { + QHashIterator i(_groupPermissions); + while (i.hasNext()) { + i.next(); + NodePermissionsPointer perms = i.value(); + if (!perms->getGroupID().isNull()) { + // we already know this group's ID + continue; + } + + // make a call to metaverse api to turn the group name into a group ID + getGroupID(perms->getID()); + } +} + +void DomainServerSettingsManager::getGroupID(const QString& groupname) { + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "getGroupIDJSONCallback"; + callbackParams.errorCallbackReceiver = this; + callbackParams.errorCallbackMethod = "getGroupIDErrorCallback"; + + const QString GET_GROUP_ID_PATH = "api/v1/get_group_id/%1"; + + qDebug() << "Requesting group ID for group named" << groupname; + + DependencyManager::get()->sendRequest(GET_GROUP_ID_PATH.arg(groupname), + AccountManagerAuth::None, + QNetworkAccessManager::GetOperation, callbackParams); +} + +void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + + qDebug() << "GOT RESPONSE" << jsonObject["group_id"].toString(); + + if (jsonObject["status"].toString() == "success") { + QString groupName = jsonObject["group_name"].toString(); + QUuid groupID = QUuid(jsonObject["group_id"].toString()); + + if (!_groupPermissions.contains(groupName)) { + qDebug() << "DomainServerSettingsManager::getGroupIDJSONCallback got response for unknown group:" << groupName; + } + + _groupPermissions[groupName]->setGroupID(groupID); + } else { + // XXX what? + } +} + +void DomainServerSettingsManager::getGroupIDErrorCallback(QNetworkReply& requestReply) { + qDebug() << "ERROR" << requestReply.error(); +} From 6fae35d9f293d027f5c7e2bbbadd84857487e126 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Jun 2016 16:42:15 -0700 Subject: [PATCH 06/98] start on handling group permissions --- domain-server/src/DomainServerSettingsManager.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 446e9a2eed..ca9973ba5d 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -27,6 +27,7 @@ const QString SETTINGS_PATH = "/settings"; const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json"; const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permissions"; const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions"; +const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions"; class DomainServerSettingsManager : public QObject { Q_OBJECT @@ -50,6 +51,9 @@ public: signals: void updateNodePermissions(); +public slots: + void getGroupIDJSONCallback(QNetworkReply& requestReply); + void getGroupIDErrorCallback(QNetworkReply& requestReply); private slots: void processSettingsRequestPacket(QSharedPointer message); @@ -72,11 +76,15 @@ private: friend class DomainServer; + void requestMissingGroupIDs(); + void getGroupID(const QString& groupname); + void packPermissionsForMap(QString mapName, QHash agentPermissions, QString keyPath); void packPermissions(); void unpackPermissions(); QHash _standardAgentPermissions; // anonymous, logged-in, localhost QHash _agentPermissions; // specific account-names + QHash _groupPermissions; // groups }; #endif // hifi_DomainServerSettingsManager_h From c80ca825b8be0de8ee10ab76bbcab18c70abe57b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Jun 2016 16:43:08 -0700 Subject: [PATCH 07/98] start on handling group permissions --- libraries/networking/src/NodePermissions.cpp | 40 +++++++++++++++++++- libraries/networking/src/NodePermissions.h | 38 +++++-------------- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index fb74ccdc94..9f77dd9d21 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -22,6 +22,45 @@ QStringList NodePermissions::standardNames = QList() << NodePermissions::standardNameLoggedIn << NodePermissions::standardNameAnonymous; +NodePermissions::NodePermissions(QMap perms) { + _id = perms["permissions_id"].toString(); + if (perms.contains("group_id")) { + _groupIDSet = true; + _groupID = perms["group_id"].toUuid(); + } + + 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(); +} + +QVariant NodePermissions::toVariant() { + QMap values; + values["permissions_id"] = _id; + if (_groupIDSet) { + values["group_id"] = _groupID; + } + 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); +} + +void NodePermissions::setAll(bool value) { + canConnectToDomain = value; + canAdjustLocks = value; + canRezPermanentEntities = value; + canRezTemporaryEntities = value; + canWriteToAssetServer = value; + canConnectPastMaxCapacity = value; +} + NodePermissions& NodePermissions::operator|=(const NodePermissions& rhs) { this->canConnectToDomain |= rhs.canConnectToDomain; this->canAdjustLocks |= rhs.canAdjustLocks; @@ -44,7 +83,6 @@ NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, const NodePermis return lhs; } - QDataStream& operator<<(QDataStream& out, const NodePermissions& perms) { out << perms.canConnectToDomain; out << perms.canAdjustLocks; diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index c153878a7e..a18ccdf97d 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -25,15 +25,7 @@ class NodePermissions { public: NodePermissions() { _id = QUuid::createUuid().toString(); } NodePermissions(const QString& name) { _id = name; } - NodePermissions(QMap perms) { - _id = perms["permissions_id"].toString(); - canConnectToDomain = perms["id_can_connect"].toBool(); - canAdjustLocks = perms["id_can_adjust_locks"].toBool(); - canRezPermanentEntities = perms["id_can_rez"].toBool(); - canRezTemporaryEntities = perms["id_can_rez_tmp"].toBool(); - canWriteToAssetServer = perms["id_can_write_to_asset_server"].toBool(); - canConnectPastMaxCapacity = perms["id_can_connect_past_max_capacity"].toBool(); - } + NodePermissions(QMap perms); QString getID() const { return _id; } @@ -41,6 +33,10 @@ public: void setUserName(QString userName) { _userName = userName; } QString getUserName() { return _userName; } + void setGroupID(QUuid groupID) { _groupID = groupID; } + QUuid getGroupID() { return _groupID; } + bool isGroup() { return _groupIDSet; } + bool isAssignment { false }; // these 3 names have special meaning. @@ -57,26 +53,9 @@ public: bool canWriteToAssetServer { false }; bool canConnectPastMaxCapacity { false }; - void setAll(bool value) { - canConnectToDomain = value; - canAdjustLocks = value; - canRezPermanentEntities = value; - canRezTemporaryEntities = value; - canWriteToAssetServer = value; - canConnectPastMaxCapacity = value; - } + QVariant toVariant(); - QVariant toVariant() { - QMap values; - values["permissions_id"] = _id; - values["id_can_connect"] = canConnectToDomain; - values["id_can_adjust_locks"] = canAdjustLocks; - values["id_can_rez"] = canRezPermanentEntities; - values["id_can_rez_tmp"] = canRezTemporaryEntities; - values["id_can_write_to_asset_server"] = canWriteToAssetServer; - values["id_can_connect_past_max_capacity"] = canConnectPastMaxCapacity; - return QVariant(values); - } + void setAll(bool value); NodePermissions& operator|=(const NodePermissions& rhs); NodePermissions& operator|=(const NodePermissionsPointer& rhs); @@ -86,6 +65,9 @@ public: protected: QString _id; QString _userName; + + bool _groupIDSet { false }; + QUuid _groupID; }; const NodePermissions DEFAULT_AGENT_PERMISSIONS; From 3fc9bdb3d00fe3e7e5dffd1b574e96cf09f3f739 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Jun 2016 16:44:18 -0700 Subject: [PATCH 08/98] undo accidental change --- domain-server/resources/web/settings/js/settings.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 48312e0e84..5fc126fbea 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1,7 +1,6 @@ var Settings = { showAdvanced: false, - // METAVERSE_URL: 'https://metaverse.highfidelity.com', - METAVERSE_URL: 'http://localhost:3000', + METAVERSE_URL: 'https://metaverse.highfidelity.com', ADVANCED_CLASS: 'advanced-setting', TRIGGER_CHANGE_CLASS: 'trigger-change', DATA_ROW_CLASS: 'value-row', From de100e81417c2839bdfb297675ffaf9f2ca84877 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 17 Jun 2016 13:29:42 -0700 Subject: [PATCH 09/98] after receiving a group's ID from api, save it --- .../src/DomainServerSettingsManager.cpp | 16 ++++++++-------- libraries/networking/src/NodePermissions.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index af0d5afab6..87803895a9 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -302,7 +302,7 @@ void DomainServerSettingsManager::packPermissions() { packPermissionsForMap("permissions", _agentPermissions, AGENT_PERMISSIONS_KEYPATH); // save settings for groups - packPermissionsForMap("permissions", _groupPermissions, AGENT_PERMISSIONS_KEYPATH); + packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH); persistToFile(); _configMap.loadMasterAndUserConfig(_argumentList); @@ -898,22 +898,22 @@ void DomainServerSettingsManager::getGroupID(const QString& groupname) { void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestReply) { QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); - qDebug() << "GOT RESPONSE" << jsonObject["group_id"].toString(); - if (jsonObject["status"].toString() == "success") { QString groupName = jsonObject["group_name"].toString(); QUuid groupID = QUuid(jsonObject["group_id"].toString()); - if (!_groupPermissions.contains(groupName)) { + if (_groupPermissions.contains(groupName)) { + qDebug() << "ID for group:" << groupName << "is" << groupID; + _groupPermissions[groupName]->setGroupID(groupID); + packPermissions(); + } else { qDebug() << "DomainServerSettingsManager::getGroupIDJSONCallback got response for unknown group:" << groupName; } - - _groupPermissions[groupName]->setGroupID(groupID); } else { - // XXX what? + qDebug() << "getGroupID api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); } } void DomainServerSettingsManager::getGroupIDErrorCallback(QNetworkReply& requestReply) { - qDebug() << "ERROR" << requestReply.error(); + qDebug() << "getGroupID api call failed:" << requestReply.error(); } diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index a18ccdf97d..9c8f4ace44 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -33,7 +33,7 @@ public: void setUserName(QString userName) { _userName = userName; } QString getUserName() { return _userName; } - void setGroupID(QUuid groupID) { _groupID = groupID; } + void setGroupID(QUuid groupID) { _groupID = groupID; _groupIDSet = true; } QUuid getGroupID() { return _groupID; } bool isGroup() { return _groupIDSet; } From e606ffb002f6f0964796107d739e328b33b9199d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 17 Jun 2016 18:12:03 -0700 Subject: [PATCH 10/98] use constant rather than hard-coding metaverse url --- domain-server/resources/web/settings/js/settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 5fc126fbea..9d1719adbe 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -617,7 +617,7 @@ function setupPlacesTable() { label: 'Places', html_id: Settings.PLACES_TABLE_ID, help: "The following places currently point to this domain.
To point places to this domain, " - + " go to the My Places " + + " go to the My Places " + "page in your High Fidelity Metaverse account.", read_only: true, columns: [ @@ -759,7 +759,7 @@ function chooseFromHighFidelityDomains(clickedButton) { modal_buttons["success"] = { label: 'Create new domain', callback: function() { - window.open("https://metaverse.highfidelity.com/user/domains", '_blank'); + window.open(Settings.METAVERSE_URL + "/user/domains", '_blank'); } } modal_body = "

You do not have any domains in your High Fidelity account." + From af52f6da823bbd87aef644af250766aa86608b60 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 20 Jun 2016 13:59:56 -0700 Subject: [PATCH 11/98] remove some duplicate the's --- domain-server/resources/describe-settings.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index a12e83df78..4143e6fc8a 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -170,7 +170,7 @@ "name": "standard_permissions", "type": "table", "label": "Domain-Wide User Permissions", - "help": "Indicate which users or groups can have which domain-wide permissions.", + "help": "Indicate which users or groups can have which domain-wide permissions.", "caption": "Standard Permissions", "can_add_new_rows": false, @@ -180,7 +180,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], @@ -249,7 +249,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], @@ -315,7 +315,7 @@ "span": 2 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], From 82dd36459d81ba7a69a055309e24a09957e975d8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 20 Jun 2016 14:02:04 -0700 Subject: [PATCH 12/98] apply a group's permissions to users who belong to the group --- domain-server/src/DomainGatekeeper.cpp | 139 ++++++++++++++++--------- 1 file changed, 92 insertions(+), 47 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 74269a5794..213a0a1cc4 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -120,6 +120,45 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLocalhost); + qDebug() << "user-permissions: is local user, so:" << userPerms; + } + + if (verifiedUsername.isEmpty()) { + userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous); + qDebug() << "user-permissions: unverified or no username, so:" << userPerms; + } else { + userPerms.setUserName(verifiedUsername); + if (_server->_settingsManager.havePermissionsForName(verifiedUsername)) { + userPerms = _server->_settingsManager.getPermissionsForName(verifiedUsername); + qDebug() << "user-permissions: specific user matches, so:" << userPerms; + } else { + // they are logged into metaverse, but we don't have specific permissions for them. + userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn); + qDebug() << "user-permissions: user is logged-into metavers, so:" << userPerms; + + // if this user is a known member of a group, give them the implied permissions + foreach (QUuid groupID, _server->_settingsManager.getKnownGroupIDs()) { + if (groupID.isNull()) { + continue; + } + if (_server->_settingsManager.isGroupMember(verifiedUsername, groupID)) { + userPerms |= _server->_settingsManager.getPermissionsForGroup(groupID); + qDebug() << "user-permissions: user is in group:" << groupID << "so:" << userPerms; + } + } + } + } + + qDebug() << "user-permissions: final:" << userPerms; + return userPerms; +} + void DomainGatekeeper::updateNodePermissions() { // If the permissions were changed on the domain-server webpage (and nothing else was), a restart isn't required -- // we reprocess the permissions map and update the nodes here. The node list is frequently sent out to all @@ -129,6 +168,8 @@ void DomainGatekeeper::updateNodePermissions() { auto limitedNodeList = DependencyManager::get(); limitedNodeList->eachNode([this, limitedNodeList, &nodesToKill](const SharedNodePointer& node){ + // the id and the username in NodePermissions will often be the same, but id is set before + // authentication and username is only set once they user's key has been confirmed. QString username = node->getPermissions().getUserName(); NodePermissions userPerms(username); @@ -140,24 +181,10 @@ void DomainGatekeeper::updateNodePermissions() { userPerms.canRezTemporaryEntities = true; } else { // this node is an agent - userPerms.setAll(false); - const QHostAddress& addr = node->getLocalSocket().getAddress(); bool isLocalUser = (addr == limitedNodeList->getLocalSockAddr().getAddress() || addr == QHostAddress::LocalHost); - if (isLocalUser) { - userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLocalhost); - } - - if (username.isEmpty()) { - userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous); - } else { - if (_server->_settingsManager.havePermissionsForName(username)) { - userPerms = _server->_settingsManager.getPermissionsForName(username); - } else { - userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn); - } - } + userPerms = applyPermissionsForUser(isLocalUser, userPerms, username); } node->setPermissions(userPerms); @@ -241,52 +268,28 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect QHostAddress senderHostAddress = nodeConnection.senderSockAddr.getAddress(); bool isLocalUser = (senderHostAddress == limitedNodeList->getLocalSockAddr().getAddress() || senderHostAddress == QHostAddress::LocalHost); - if (isLocalUser) { - userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLocalhost); - qDebug() << "user-permissions: is local user, so:" << userPerms; - } if (!username.isEmpty() && usernameSignature.isEmpty()) { // user is attempting to prove their identity to us, but we don't have enough information sendConnectionTokenPacket(username, nodeConnection.senderSockAddr); // ask for their public key right now to make sure we have it requestUserPublicKey(username); - if (!userPerms.canConnectToDomain) { - return SharedNodePointer(); - } + return SharedNodePointer(); } - if (username.isEmpty()) { - // they didn't tell us who they are - userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous); - qDebug() << "user-permissions: no username, so:" << userPerms; - } else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { + QString verifiedUsername; + if (!username.isEmpty() && verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { // they are sent us a username and the signature verifies it - - - // TODO check groups here - - - if (_server->_settingsManager.havePermissionsForName(username)) { - // we have specific permissions for this user. Discard any other permissions and set this - // agent's permissions to be exactly what's indicated on their row of the permissions-grid. - userPerms = _server->_settingsManager.getPermissionsForName(username); - qDebug() << "user-permissions: specific user matches, so:" << userPerms; - } else { - // they are logged into metaverse, but we don't have specific permissions for them. - userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn); - qDebug() << "user-permissions: user is logged in, so:" << userPerms; - } userPerms.setUserName(username); - } else { + verifiedUsername = username; + getGroupMemberships(username); + } else if (!username.isEmpty()) { // they sent us a username, but it didn't check out requestUserPublicKey(username); - if (!userPerms.canConnectToDomain) { - return SharedNodePointer(); - } + return SharedNodePointer(); } - qDebug() << "user-permissions: final:" << userPerms; + userPerms = applyPermissionsForUser(isLocalUser, userPerms, verifiedUsername); if (!userPerms.canConnectToDomain) { sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.", @@ -651,3 +654,45 @@ void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer sendingPeer->activateMatchingOrNewSymmetricSocket(message->getSenderSockAddr()); } } + +void DomainGatekeeper::getGroupMemberships(const QString& username) { + // loop through the groups mentioned on the settings page and ask if this user is in each. The replies + // will be received asynchronously and permissions will be updated as the answers come in. + QList groupIDs = _server->_settingsManager.getKnownGroupIDs(); + foreach (QUuid groupID, groupIDs) { + if (groupID.isNull()) { + continue; + } + getIsGroupMember(username, groupID); + } +} + +void DomainGatekeeper::getIsGroupMember(const QString& username, const QUuid groupID) { + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "getIsGroupMemberJSONCallback"; + callbackParams.errorCallbackReceiver = this; + callbackParams.errorCallbackMethod = "getIsGroupMemberErrorCallback"; + + const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/%1/is_member/%2"; + QString groupIDStr = groupID.toString().mid(1,36); + DependencyManager::get()->sendRequest(GET_IS_GROUP_MEMBER_PATH.arg(groupIDStr).arg(username), + AccountManagerAuth::None, + QNetworkAccessManager::GetOperation, callbackParams); +} + +void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + if (jsonObject["status"].toString() == "success") { + QString username = jsonObject["username"].toString(); + QUuid groupID = QUuid(jsonObject["group_id"].toString()); + bool isMember = jsonObject["is_member"].toBool(); + _server->_settingsManager.recordGroupMembership(username, groupID, isMember); + } else { + qDebug() << "getIsGroupMember api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); + } +} + +void DomainGatekeeper::getIsGroupMemberErrorCallback(QNetworkReply& requestReply) { + qDebug() << "getGroupID api call failed:" << requestReply.error(); +} From fdafbd2015e72f11b7b3a07176dd439e2078d456 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 20 Jun 2016 14:02:23 -0700 Subject: [PATCH 13/98] apply a group's permissions to users who belong to the group --- domain-server/src/DomainGatekeeper.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 50bbf38543..0f8ce70cf3 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -51,7 +51,10 @@ public slots: void processICEPeerInformationPacket(QSharedPointer message); void publicKeyJSONCallback(QNetworkReply& requestReply); - + + void getIsGroupMemberJSONCallback(QNetworkReply& requestReply); + void getIsGroupMemberErrorCallback(QNetworkReply& requestReply); + signals: void killNode(SharedNodePointer node); void connectedNode(SharedNodePointer node); @@ -93,6 +96,10 @@ private: QHash _connectionTokenHash; QHash _userPublicKeys; + + NodePermissions applyPermissionsForUser(bool isLocalUser, NodePermissions userPerms, QString verifiedUsername); + void getGroupMemberships(const QString& username); + void getIsGroupMember(const QString& username, const QUuid groupID); }; From ba032ce0b4a04da02189a1c85f2a7a92a1d65012 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 20 Jun 2016 14:06:09 -0700 Subject: [PATCH 14/98] apply a group's permissions to users who belong to the group --- .../src/DomainServerSettingsManager.cpp | 42 ++++++++++++++++++- .../src/DomainServerSettingsManager.h | 16 ++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index ff38af6ad9..5e55a20601 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -379,6 +379,10 @@ void DomainServerSettingsManager::unpackPermissions() { } else { _groupPermissions[id] = perms; } + if (perms->isGroup()) { + // the group-id was cached. hook-up the id in the id->group hash + _groupByID[perms->getGroupID()] = _groupPermissions[id]; + } } // if any of the standard names are missing, add them @@ -444,6 +448,26 @@ NodePermissions DomainServerSettingsManager::getPermissionsForName(const QString return nullPermissions; } +NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QString& groupname) const { + if (_groupPermissions.contains(groupname)) { + return *(_groupPermissions[groupname].get()); + } + NodePermissions nullPermissions; + nullPermissions.setAll(false); + return nullPermissions; +} + +NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QUuid& groupID) const { + if (!_groupByID.contains(groupID)) { + NodePermissions nullPermissions; + nullPermissions.setAll(false); + return nullPermissions; + } + QString groupName = _groupByID[groupID]->getID(); + return getPermissionsForGroup(groupName); +} + + QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath); @@ -865,7 +889,7 @@ void DomainServerSettingsManager::persistToFile() { } void DomainServerSettingsManager::requestMissingGroupIDs() { - QHashIterator i(_groupPermissions); + QHashIterator i(_groupPermissions.get()); while (i.hasNext()) { i.next(); NodePermissionsPointer perms = i.value(); @@ -879,6 +903,13 @@ void DomainServerSettingsManager::requestMissingGroupIDs() { } } +NodePermissionsPointer DomainServerSettingsManager::lookupGroupByID(const QUuid& id) { + if (_groupByID.contains(id)) { + return _groupByID[id]; + } + return nullptr; +} + void DomainServerSettingsManager::getGroupID(const QString& groupname) { JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; @@ -905,6 +936,7 @@ void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestR if (_groupPermissions.contains(groupName)) { qDebug() << "ID for group:" << groupName << "is" << groupID; _groupPermissions[groupName]->setGroupID(groupID); + _groupByID[groupID] = _groupPermissions[groupName]; packPermissions(); } else { qDebug() << "DomainServerSettingsManager::getGroupIDJSONCallback got response for unknown group:" << groupName; @@ -917,3 +949,11 @@ void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestR void DomainServerSettingsManager::getGroupIDErrorCallback(QNetworkReply& requestReply) { qDebug() << "getGroupID api call failed:" << requestReply.error(); } + +void DomainServerSettingsManager::recordGroupMembership(const QString& name, const QUuid groupID, bool isMember) { + _groupMembership[name][groupID] = isMember; +} + +bool DomainServerSettingsManager::isGroupMember(const QString& name, const QUuid& groupID) { + return _groupMembership[name][groupID]; +} diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index a438e1160a..979d047fb7 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -43,11 +43,21 @@ public: QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); } bool haveStandardPermissionsForName(const QString& name) const { return _standardAgentPermissions.contains(name); } - bool havePermissionsForName(const QString& name) const { return _agentPermissions.contains(name); } NodePermissions getStandardPermissionsForName(const QString& name) const; + + bool havePermissionsForName(const QString& name) const { return _agentPermissions.contains(name); } NodePermissions getPermissionsForName(const QString& name) const; QStringList getAllNames() { return _agentPermissions.keys(); } + bool havePermissionsForGroup(const QString& groupname) const { return _groupPermissions.contains(groupname); } + NodePermissions getPermissionsForGroup(const QString& groupname) const; + NodePermissions getPermissionsForGroup(const QUuid& groupID) const; + QList getKnownGroupIDs() { return _groupByID.keys(); } + + // these are used to locally cache the result of calling "api/v1/groups/%1/is_member/%2" on metaverse's api + void recordGroupMembership(const QString& name, const QUuid groupID, bool isMember); + bool isGroupMember(const QString& name, const QUuid& groupID); + signals: void updateNodePermissions(); @@ -78,12 +88,16 @@ private: void requestMissingGroupIDs(); void getGroupID(const QString& groupname); + NodePermissionsPointer lookupGroupByID(const QUuid& id); void packPermissionsForMap(QString mapName, NodePermissionsMap& agentPermissions, QString keyPath); void packPermissions(); void unpackPermissions(); NodePermissionsMap _standardAgentPermissions; // anonymous, logged-in, localhost NodePermissionsMap _agentPermissions; // specific account-names + NodePermissionsMap _groupPermissions; // permissions granted by membershipt to specific groups + QHash _groupByID; + QHash> _groupMembership; }; #endif // hifi_DomainServerSettingsManager_h From b1ad743721d6ac5be20d051550770b72960ba801 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 20 Jun 2016 14:06:45 -0700 Subject: [PATCH 15/98] apply a group's permissions to users who belong to the group --- libraries/networking/src/NodePermissions.h | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index b906ab6f12..c2022bd7ff 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -25,15 +25,7 @@ class NodePermissions { public: NodePermissions() { _id = QUuid::createUuid().toString(); } NodePermissions(const QString& name) { _id = name.toLower(); } - NodePermissions(QMap perms) { - _id = perms["permissions_id"].toString().toLower(); - 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(); - } + NodePermissions(QMap perms); QString getID() const { return _id; } @@ -41,7 +33,7 @@ public: void setUserName(QString userName) { _userName = userName.toLower(); } QString getUserName() { return _userName; } - void setGroupID(QUuid groupID) { _groupID = groupID; _groupIDSet = true; } + void setGroupID(QUuid groupID) { _groupID = groupID; if (!groupID.isNull()) { _groupIDSet = true; } } QUuid getGroupID() { return _groupID; } bool isGroup() { return _groupIDSet; } From f397c4760f3f4046af87a2403c15503b0ffec2f6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 21 Jun 2016 09:56:39 -0700 Subject: [PATCH 16/98] comments --- domain-server/src/DomainServerSettingsManager.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 979d047fb7..a67f3d4338 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -42,19 +42,22 @@ public: QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); } QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); } + // these give access to anonymous/localhost/logged-in settings from the domain-server settings page bool haveStandardPermissionsForName(const QString& name) const { return _standardAgentPermissions.contains(name); } NodePermissions getStandardPermissionsForName(const QString& name) const; + // these give access to permissions for specific user-names from the domain-server settings page bool havePermissionsForName(const QString& name) const { return _agentPermissions.contains(name); } NodePermissions getPermissionsForName(const QString& name) const; QStringList getAllNames() { return _agentPermissions.keys(); } + // these give access to permissions for specific groups from the domain-server settings page bool havePermissionsForGroup(const QString& groupname) const { return _groupPermissions.contains(groupname); } NodePermissions getPermissionsForGroup(const QString& groupname) const; NodePermissions getPermissionsForGroup(const QUuid& groupID) const; QList getKnownGroupIDs() { return _groupByID.keys(); } - // these are used to locally cache the result of calling "api/v1/groups/%1/is_member/%2" on metaverse's api + // these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api void recordGroupMembership(const QString& name, const QUuid groupID, bool isMember); bool isGroupMember(const QString& name, const QUuid& groupID); @@ -86,6 +89,7 @@ private: friend class DomainServer; + // these cause calls to metaverse's group api void requestMissingGroupIDs(); void getGroupID(const QString& groupname); NodePermissionsPointer lookupGroupByID(const QUuid& id); @@ -93,10 +97,13 @@ private: void packPermissionsForMap(QString mapName, NodePermissionsMap& agentPermissions, QString keyPath); void packPermissions(); void unpackPermissions(); + NodePermissionsMap _standardAgentPermissions; // anonymous, logged-in, localhost NodePermissionsMap _agentPermissions; // specific account-names NodePermissionsMap _groupPermissions; // permissions granted by membershipt to specific groups - QHash _groupByID; + QHash _groupByID; // similar to _groupPermissions but key is group-id rather than name + + // keep track of answers to api queries about which users are in which groups QHash> _groupMembership; }; From c97618f8bc6070ec12ba1c8607cd30f9379155e5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 21 Jun 2016 11:40:55 -0700 Subject: [PATCH 17/98] reduce the number of user public-key downloads during a login attempt --- domain-server/src/DomainGatekeeper.cpp | 53 ++++++++++++++++++-------- domain-server/src/DomainGatekeeper.h | 2 + 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 213a0a1cc4..a8b146e12b 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -274,6 +274,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect sendConnectionTokenPacket(username, nodeConnection.senderSockAddr); // ask for their public key right now to make sure we have it requestUserPublicKey(username); + getGroupMemberships(username); // optimistically get started on group memberships return SharedNodePointer(); } @@ -480,10 +481,20 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) { return; } + QString lowerUsername = username.toLower(); + if (_inFlightPublicKeyRequests.contains(lowerUsername)) { + // public-key request for this username is already flight, not rerequesting + return; + } + _inFlightPublicKeyRequests[lowerUsername] = true; + // even if we have a public key for them right now, request a new one in case it has just changed JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; callbackParams.jsonCallbackMethod = "publicKeyJSONCallback"; + callbackParams.errorCallbackReceiver = this; + callbackParams.errorCallbackMethod = "publicKeyJSONErrorCallback"; + const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; @@ -494,28 +505,40 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) { QNetworkAccessManager::GetOperation, callbackParams); } +QString extractUsernameFromPublicKeyRequest(QNetworkReply& requestReply) { + // extract the username from the request url + QString username; + const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key"; + QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING); + if (usernameRegex.indexIn(requestReply.url().toString()) != -1) { + username = usernameRegex.cap(1); + } + return username.toLower(); +} + void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) { QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + QString username = extractUsernameFromPublicKeyRequest(requestReply); - if (jsonObject["status"].toString() == "success") { + if (jsonObject["status"].toString() == "success" && username != "") { // figure out which user this is for - const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key"; - QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING); + qDebug() << "Storing a public key for user" << username; + // pull the public key as a QByteArray from this response + const QString JSON_DATA_KEY = "data"; + const QString JSON_PUBLIC_KEY_KEY = "public_key"; - if (usernameRegex.indexIn(requestReply.url().toString()) != -1) { - QString username = usernameRegex.cap(1); - - qDebug() << "Storing a public key for user" << username; - - // pull the public key as a QByteArray from this response - const QString JSON_DATA_KEY = "data"; - const QString JSON_PUBLIC_KEY_KEY = "public_key"; - - _userPublicKeys[username] = - QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); - } + _userPublicKeys[username] = + QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); } + + _inFlightPublicKeyRequests.remove(username); +} + +void DomainGatekeeper::publicKeyJSONErrorCallback(QNetworkReply& requestReply) { + qDebug() << "publicKey api call failed:" << requestReply.error(); + QString username = extractUsernameFromPublicKeyRequest(requestReply); + _inFlightPublicKeyRequests.remove(username); } void DomainGatekeeper::sendProtocolMismatchConnectionDenial(const HifiSockAddr& senderSockAddr) { diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 0f8ce70cf3..bc8a569591 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -51,6 +51,7 @@ public slots: void processICEPeerInformationPacket(QSharedPointer message); void publicKeyJSONCallback(QNetworkReply& requestReply); + void publicKeyJSONErrorCallback(QNetworkReply& requestReply); void getIsGroupMemberJSONCallback(QNetworkReply& requestReply); void getIsGroupMemberErrorCallback(QNetworkReply& requestReply); @@ -96,6 +97,7 @@ private: QHash _connectionTokenHash; QHash _userPublicKeys; + QHash _inFlightPublicKeyRequests; // keep track of which we've already asked for NodePermissions applyPermissionsForUser(bool isLocalUser, NodePermissions userPerms, QString verifiedUsername); void getGroupMemberships(const QString& username); From cc1b6f0cb2b70158114edbab2721137509c82c69 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 24 Jun 2016 11:42:56 -0700 Subject: [PATCH 18/98] add a permissions-grid row for friends-of-domain-owner --- domain-server/src/DomainGatekeeper.cpp | 44 ++++++++++++++++++- domain-server/src/DomainGatekeeper.h | 5 +++ .../src/DomainServerSettingsManager.cpp | 9 ++++ libraries/networking/src/AccountManager.cpp | 42 ++++++++++-------- libraries/networking/src/AccountManager.h | 4 +- libraries/networking/src/NodePermissions.cpp | 4 +- libraries/networking/src/NodePermissions.h | 1 + 7 files changed, 87 insertions(+), 22 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index a8b146e12b..90d0d0adca 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -142,6 +142,12 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn); qDebug() << "user-permissions: user is logged-into metavers, so:" << userPerms; + // if this user is a friend of the domain-owner, give them friend's permissions + if (_domainOwnerFriends.contains(verifiedUsername)) { + userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameFriends); + qDebug() << "user-permissions: user is friends with domain-owner, so:" << userPerms; + } + // if this user is a known member of a group, give them the implied permissions foreach (QUuid groupID, _server->_settingsManager.getKnownGroupIDs()) { if (groupID.isNull()) { @@ -275,6 +281,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect // ask for their public key right now to make sure we have it requestUserPublicKey(username); getGroupMemberships(username); // optimistically get started on group memberships + getDomainOwnerFriendsList(); return SharedNodePointer(); } @@ -284,6 +291,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect userPerms.setUserName(username); verifiedUsername = username; getGroupMemberships(username); + getDomainOwnerFriendsList(); } else if (!username.isEmpty()) { // they sent us a username, but it didn't check out requestUserPublicKey(username); @@ -717,5 +725,39 @@ void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) } void DomainGatekeeper::getIsGroupMemberErrorCallback(QNetworkReply& requestReply) { - qDebug() << "getGroupID api call failed:" << requestReply.error(); + qDebug() << "getIsGroupMember api call failed:" << requestReply.error(); +} + +void DomainGatekeeper::getDomainOwnerFriendsList() { + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "getDomainOwnerFriendsListJSONCallback"; + callbackParams.errorCallbackReceiver = this; + callbackParams.errorCallbackMethod = "getDomainOwnerFriendsListErrorCallback"; + + const QString GET_FRIENDS_LIST_PATH = "api/v1/users"; + QUrlQuery query; + query.addQueryItem("filter", "friends"); + + DependencyManager::get()->sendRequest(GET_FRIENDS_LIST_PATH, AccountManagerAuth::Required, + QNetworkAccessManager::GetOperation, callbackParams, QByteArray(), + NULL, QVariantMap(), query); +} + +void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + if (jsonObject["status"].toString() == "success") { + _domainOwnerFriends.clear(); + QJsonArray friends = jsonObject["data"].toObject()["users"].toArray(); + for (int i = 0; i < friends.size(); i++) { + QString friendUserName = friends.at(i).toObject()["username"].toString(); + _domainOwnerFriends[friendUserName] = true; + } + } else { + qDebug() << "getDomainOwnerFriendsList api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); + } +} + +void DomainGatekeeper::getDomainOwnerFriendsListErrorCallback(QNetworkReply& requestReply) { + qDebug() << "getDomainOwnerFriendsList api call failed:" << requestReply.error(); } diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index bc8a569591..7d9cfe91e3 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -56,6 +56,9 @@ public slots: void getIsGroupMemberJSONCallback(QNetworkReply& requestReply); void getIsGroupMemberErrorCallback(QNetworkReply& requestReply); + void getDomainOwnerFriendsListJSONCallback(QNetworkReply& requestReply); + void getDomainOwnerFriendsListErrorCallback(QNetworkReply& requestReply); + signals: void killNode(SharedNodePointer node); void connectedNode(SharedNodePointer node); @@ -98,10 +101,12 @@ private: QHash _connectionTokenHash; QHash _userPublicKeys; QHash _inFlightPublicKeyRequests; // keep track of which we've already asked for + QHash _domainOwnerFriends; // keep track of friends of the domain owner NodePermissions applyPermissionsForUser(bool isLocalUser, NodePermissions userPerms, QString verifiedUsername); void getGroupMemberships(const QString& username); void getIsGroupMember(const QString& username, const QUuid groupID); + void getDomainOwnerFriendsList(); }; diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index d7fcf324e8..78a557ba5b 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -218,6 +218,8 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList new NodePermissions(NodePermissions::standardNameAnonymous)); _standardAgentPermissions[NodePermissions::standardNameLoggedIn].reset( new NodePermissions(NodePermissions::standardNameLoggedIn)); + _standardAgentPermissions[NodePermissions::standardNameFriends].reset( + new NodePermissions(NodePermissions::standardNameFriends)); if (isRestrictedAccess) { // only users in allow-users list can connect @@ -340,6 +342,7 @@ void DomainServerSettingsManager::unpackPermissions() { bool foundLocalhost = false; bool foundAnonymous = false; bool foundLoggedIn = false; + bool foundFriends = false; bool needPack = false; QVariant* standardPermissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_STANDARD_PERMISSIONS_KEYPATH); @@ -368,6 +371,7 @@ void DomainServerSettingsManager::unpackPermissions() { foundLocalhost |= (id == NodePermissions::standardNameLocalhost); foundAnonymous |= (id == NodePermissions::standardNameAnonymous); foundLoggedIn |= (id == NodePermissions::standardNameLoggedIn); + foundFriends |= (id == NodePermissions::standardNameFriends); if (_standardAgentPermissions.contains(id)) { qDebug() << "duplicate name in standard permissions table: " << id; _standardAgentPermissions[id] |= perms; @@ -424,6 +428,11 @@ void DomainServerSettingsManager::unpackPermissions() { _standardAgentPermissions[perms->getID()] = perms; needPack = true; } + if (!foundFriends) { + NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameFriends) }; + _standardAgentPermissions[perms->getID()] = perms; + needPack = true; + } if (needPack) { packPermissions(); diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index b6c5e691a6..d7976e2036 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -200,8 +200,9 @@ void AccountManager::sendRequest(const QString& path, const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart, - const QVariantMap& propertyMap) { - + const QVariantMap& propertyMap, + QUrlQuery query) { + if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, "sendRequest", Q_ARG(const QString&, path), @@ -213,9 +214,9 @@ void AccountManager::sendRequest(const QString& path, Q_ARG(QVariantMap, propertyMap)); return; } - + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - + QNetworkRequest networkRequest; networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter()); @@ -228,13 +229,17 @@ void AccountManager::sendRequest(const QString& path, } QUrl requestURL = _authURL; - + if (path.startsWith("/")) { requestURL.setPath(path); } else { requestURL.setPath("/" + path); } - + + if (!query.isEmpty()) { + requestURL.setQuery(query); + } + if (authType != AccountManagerAuth::None ) { if (hasValidAccessToken()) { networkRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, @@ -245,22 +250,21 @@ void AccountManager::sendRequest(const QString& path, << path << "that requires authentication"; return; } - } } - + networkRequest.setUrl(requestURL); - + if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qCDebug(networking) << "Making a request to" << qPrintable(requestURL.toString()); - + if (!dataByteArray.isEmpty()) { qCDebug(networking) << "The POST/PUT body -" << QString(dataByteArray); } } - + QNetworkReply* networkReply = NULL; - + switch (operation) { case QNetworkAccessManager::GetOperation: networkReply = networkAccessManager.get(networkRequest); @@ -273,7 +277,7 @@ void AccountManager::sendRequest(const QString& path, } else { networkReply = networkAccessManager.put(networkRequest, dataMultiPart); } - + // make sure dataMultiPart is destroyed when the reply is connect(networkReply, &QNetworkReply::destroyed, dataMultiPart, &QHttpMultiPart::deleteLater); } else { @@ -284,7 +288,7 @@ void AccountManager::sendRequest(const QString& path, networkReply = networkAccessManager.put(networkRequest, dataByteArray); } } - + break; case QNetworkAccessManager::DeleteOperation: networkReply = networkAccessManager.sendCustomRequest(networkRequest, "DELETE"); @@ -293,7 +297,7 @@ void AccountManager::sendRequest(const QString& path, // other methods not yet handled break; } - + if (networkReply) { if (!propertyMap.isEmpty()) { // we have properties to set on the reply so the user can check them after @@ -301,18 +305,18 @@ void AccountManager::sendRequest(const QString& path, networkReply->setProperty(qPrintable(propertyKey), propertyMap.value(propertyKey)); } } - - + + if (!callbackParams.isEmpty()) { // if we have information for a callback, insert the callbackParams into our local map _pendingCallbackMap.insert(networkReply, callbackParams); - + if (callbackParams.updateReciever && !callbackParams.updateSlot.isEmpty()) { callbackParams.updateReciever->connect(networkReply, SIGNAL(uploadProgress(qint64, qint64)), callbackParams.updateSlot.toStdString().c_str()); } } - + // if we ended up firing of a request, hook up to it now connect(networkReply, SIGNAL(finished()), SLOT(processReply())); } diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index d30a05fb2c..ef66164b12 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "NetworkAccessManager.h" @@ -67,7 +68,8 @@ public: const JSONCallbackParameters& callbackParams = JSONCallbackParameters(), const QByteArray& dataByteArray = QByteArray(), QHttpMultiPart* dataMultiPart = NULL, - const QVariantMap& propertyMap = QVariantMap()); + const QVariantMap& propertyMap = QVariantMap(), + QUrlQuery query = QUrlQuery()); void setIsAgent(bool isAgent) { _isAgent = isAgent; } diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index 9f77dd9d21..f0c25a764a 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -16,11 +16,13 @@ QString NodePermissions::standardNameLocalhost = QString("localhost"); QString NodePermissions::standardNameLoggedIn = QString("logged-in"); QString NodePermissions::standardNameAnonymous = QString("anonymous"); +QString NodePermissions::standardNameFriends = QString("friends"); QStringList NodePermissions::standardNames = QList() << NodePermissions::standardNameLocalhost << NodePermissions::standardNameLoggedIn - << NodePermissions::standardNameAnonymous; + << NodePermissions::standardNameAnonymous + << NodePermissions::standardNameFriends; NodePermissions::NodePermissions(QMap perms) { _id = perms["permissions_id"].toString(); diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index c2022bd7ff..7971d4e362 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -43,6 +43,7 @@ public: static QString standardNameLocalhost; static QString standardNameLoggedIn; static QString standardNameAnonymous; + static QString standardNameFriends; static QStringList standardNames; // the initializations here should match the defaults in describe-settings.json From ead6e476a9e1acbe43a43025896f53e479e5aa10 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 24 Jun 2016 14:03:43 -0700 Subject: [PATCH 19/98] add a list of blacklist groups which remove permissions rather than grant them --- .../resources/describe-settings.json | 71 ++++++++++++ domain-server/src/DomainGatekeeper.cpp | 16 ++- .../src/DomainServerSettingsManager.cpp | 102 +++++++++++++++++- .../src/DomainServerSettingsManager.h | 13 ++- libraries/networking/src/NodePermissions.cpp | 42 ++++++++ libraries/networking/src/NodePermissions.h | 5 + 6 files changed, 237 insertions(+), 12 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 23a4302d2c..b24acd1bce 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -534,6 +534,77 @@ } ], + "columns": [ + { + "name": "permissions_id", + "label": "Group Name" + }, + { + "name": "group_id", + "label": "Group ID", + "readonly": true + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": true + }, + { + "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": "group_forbiddens", + "type": "table", + "caption": "Permissions denied to Users in Groups", + "can_add_new_rows": true, + + "groups": [ + { + "label": "Group", + "span": 2 + }, + { + "label": "Permissions ?", + "span": 6 + } + ], + "columns": [ { "name": "permissions_id", diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 90d0d0adca..b5372ea9bc 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -149,15 +149,21 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, } // if this user is a known member of a group, give them the implied permissions - foreach (QUuid groupID, _server->_settingsManager.getKnownGroupIDs()) { - if (groupID.isNull()) { - continue; - } + foreach (QUuid groupID, _server->_settingsManager.getGroupIDs()) { if (_server->_settingsManager.isGroupMember(verifiedUsername, groupID)) { userPerms |= _server->_settingsManager.getPermissionsForGroup(groupID); qDebug() << "user-permissions: user is in group:" << groupID << "so:" << userPerms; } } + + // if this user is a known member of a blacklist group, remove the implied permissions + foreach (QUuid groupID, _server->_settingsManager.getBlacklistGroupIDs()) { + if (_server->_settingsManager.isGroupMember(verifiedUsername, groupID)) { + userPerms &= ~_server->_settingsManager.getForbiddensForGroup(groupID); + qDebug() << "user-permissions: user is in blacklist group:" << groupID << "so:" << userPerms; + } + } + } } @@ -689,7 +695,7 @@ void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer void DomainGatekeeper::getGroupMemberships(const QString& username) { // loop through the groups mentioned on the settings page and ask if this user is in each. The replies // will be received asynchronously and permissions will be updated as the answers come in. - QList groupIDs = _server->_settingsManager.getKnownGroupIDs(); + QList groupIDs = _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs(); foreach (QUuid groupID, groupIDs) { if (groupID.isNull()) { continue; diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 78a557ba5b..2411dbdf09 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -328,6 +328,9 @@ void DomainServerSettingsManager::packPermissions() { // save settings for groups packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH); + // save settings for blacklist groups + packPermissionsForMap("permissions", _groupForbiddens, GROUP_FORBIDDENS_KEYPATH); + persistToFile(); _configMap.loadMasterAndUserConfig(_argumentList); } @@ -338,6 +341,7 @@ void DomainServerSettingsManager::unpackPermissions() { _standardAgentPermissions.clear(); _agentPermissions.clear(); _groupPermissions.clear(); + _groupForbiddens.clear(); bool foundLocalhost = false; bool foundAnonymous = false; @@ -363,6 +367,12 @@ void DomainServerSettingsManager::unpackPermissions() { groupPermissions = valueForKeyPath(_configMap.getUserConfig(), GROUP_PERMISSIONS_KEYPATH, true); (*groupPermissions) = QVariantList(); } + QVariant* groupForbiddens = valueForKeyPath(_configMap.getUserConfig(), GROUP_FORBIDDENS_KEYPATH); + if (!groupForbiddens || !groupForbiddens->canConvert(QMetaType::QVariantList)) { + qDebug() << "failed to extract group forbiddens from settings."; + groupForbiddens = valueForKeyPath(_configMap.getUserConfig(), GROUP_FORBIDDENS_KEYPATH, true); + (*groupForbiddens) = QVariantList(); + } QList standardPermissionsList = standardPermissions->toList(); foreach (QVariant permsHash, standardPermissionsList) { @@ -411,6 +421,23 @@ void DomainServerSettingsManager::unpackPermissions() { } } + QList groupForbiddensList = groupForbiddens->toList(); + foreach (QVariant permsHash, groupForbiddensList) { + NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; + QString id = perms->getID(); + if (_groupForbiddens.contains(id)) { + qDebug() << "duplicate name in group forbiddens table: " << id; + _groupForbiddens[id] |= perms; + needPack = true; + } else { + _groupForbiddens[id] = perms; + } + if (perms->isGroup()) { + // the group-id was cached. hook-up the id in the id->group hash + _groupByID[perms->getGroupID()] = _groupForbiddens[id]; + } + } + // if any of the standard names are missing, add them if (!foundLocalhost) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLocalhost) }; @@ -445,7 +472,8 @@ void DomainServerSettingsManager::unpackPermissions() { #ifdef WANT_DEBUG qDebug() << "--------------- permissions ---------------------"; QList> permissionsSets; - permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get() << _groupPermissions.get(); + permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get() + << _groupPermissions.get() << _groupForbiddens.get(); foreach (auto permissionSet, permissionsSets) { QHashIterator i(permissionSet); while (i.hasNext()) { @@ -499,6 +527,27 @@ NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QUuid& } +NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QString& groupname) const { + if (_groupForbiddens.contains(groupname)) { + return *(_groupForbiddens[groupname].get()); + } + NodePermissions nullForbiddens; + nullForbiddens.setAll(false); + return nullForbiddens; +} + +NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid& groupID) const { + if (!_groupByID.contains(groupID)) { + NodePermissions nullForbiddens; + nullForbiddens.setAll(false); + return nullForbiddens; + } + QString groupName = _groupByID[groupID]->getID(); + return getForbiddensForGroup(groupName); +} + + + QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath); @@ -920,10 +969,22 @@ void DomainServerSettingsManager::persistToFile() { } void DomainServerSettingsManager::requestMissingGroupIDs() { - QHashIterator i(_groupPermissions.get()); - while (i.hasNext()) { - i.next(); - NodePermissionsPointer perms = i.value(); + QHashIterator i_permissions(_groupPermissions.get()); + while (i_permissions.hasNext()) { + i_permissions.next(); + NodePermissionsPointer perms = i_permissions.value(); + if (!perms->getGroupID().isNull()) { + // we already know this group's ID + continue; + } + + // make a call to metaverse api to turn the group name into a group ID + getGroupID(perms->getID()); + } + QHashIterator i_forbiddens(_groupForbiddens.get()); + while (i_forbiddens.hasNext()) { + i_forbiddens.next(); + NodePermissionsPointer perms = i_forbiddens.value(); if (!perms->getGroupID().isNull()) { // we already know this group's ID continue; @@ -964,10 +1025,21 @@ void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestR QString groupName = jsonObject["group_name"].toString(); QUuid groupID = QUuid(jsonObject["group_id"].toString()); + bool found = false; if (_groupPermissions.contains(groupName)) { qDebug() << "ID for group:" << groupName << "is" << groupID; _groupPermissions[groupName]->setGroupID(groupID); _groupByID[groupID] = _groupPermissions[groupName]; + found = true; + } + if (_groupForbiddens.contains(groupName)) { + qDebug() << "ID for group:" << groupName << "is" << groupID; + _groupForbiddens[groupName]->setGroupID(groupID); + _groupByID[groupID] = _groupForbiddens[groupName]; + found = true; + } + + if (found) { packPermissions(); } else { qDebug() << "DomainServerSettingsManager::getGroupIDJSONCallback got response for unknown group:" << groupName; @@ -988,3 +1060,23 @@ void DomainServerSettingsManager::recordGroupMembership(const QString& name, con bool DomainServerSettingsManager::isGroupMember(const QString& name, const QUuid& groupID) { return _groupMembership[name][groupID]; } + +QList DomainServerSettingsManager::getGroupIDs() { + QList result; + foreach (QString groupName, _groupPermissions.keys()) { + if (_groupPermissions[groupName]->isGroup()) { + result << _groupPermissions[groupName]->getGroupID(); + } + } + return result; +} + +QList DomainServerSettingsManager::getBlacklistGroupIDs() { + QList result; + foreach (QString groupName, _groupForbiddens.keys()) { + if (_groupForbiddens[groupName]->isGroup()) { + result << _groupForbiddens[groupName]->getGroupID(); + } + } + return result; +} diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index a67f3d4338..7354955684 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -28,6 +28,7 @@ const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json"; const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permissions"; const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions"; const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions"; +const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens"; class DomainServerSettingsManager : public QObject { Q_OBJECT @@ -55,7 +56,14 @@ public: bool havePermissionsForGroup(const QString& groupname) const { return _groupPermissions.contains(groupname); } NodePermissions getPermissionsForGroup(const QString& groupname) const; NodePermissions getPermissionsForGroup(const QUuid& groupID) const; - QList getKnownGroupIDs() { return _groupByID.keys(); } + + // these remove permissions from users in certain groups + bool haveForbiddensForGroup(const QString& groupname) const { return _groupForbiddens.contains(groupname); } + NodePermissions getForbiddensForGroup(const QString& groupname) const; + NodePermissions getForbiddensForGroup(const QUuid& groupID) const; + + QList getGroupIDs(); + QList getBlacklistGroupIDs(); // these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api void recordGroupMembership(const QString& name, const QUuid groupID, bool isMember); @@ -100,7 +108,8 @@ private: NodePermissionsMap _standardAgentPermissions; // anonymous, logged-in, localhost NodePermissionsMap _agentPermissions; // specific account-names - NodePermissionsMap _groupPermissions; // permissions granted by membershipt to specific groups + NodePermissionsMap _groupPermissions; // permissions granted by membership to specific groups + NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group QHash _groupByID; // similar to _groupPermissions but key is group-id rather than name // keep track of answers to api queries about which users are in which groups diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index f0c25a764a..a6e502ff59 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -85,6 +85,48 @@ NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, const NodePermis return lhs; } +NodePermissions& NodePermissions::operator&=(const NodePermissions& 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; +} +NodePermissions& NodePermissions::operator&=(const NodePermissionsPointer& rhs) { + if (rhs) { + *this &= *rhs.get(); + } + return *this; +} +NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs) { + if (lhs && rhs) { + *lhs.get() &= rhs; + } + return lhs; +} + +NodePermissions NodePermissions::operator~() { + NodePermissions result = *this; + result.canConnectToDomain = !result.canConnectToDomain; + result.canAdjustLocks = !result.canAdjustLocks; + result.canRezPermanentEntities = !result.canRezPermanentEntities; + result.canRezTemporaryEntities = !result.canRezTemporaryEntities; + result.canWriteToAssetServer = !result.canWriteToAssetServer; + result.canConnectPastMaxCapacity = !result.canConnectPastMaxCapacity; + return result; +} + +NodePermissionsPointer operator~(NodePermissionsPointer& lhs) { + if (lhs) { + NodePermissionsPointer result { new NodePermissions }; + (*result.get()) = ~(*lhs.get()); + return result; + } + return lhs; +} + QDataStream& operator<<(QDataStream& out, const NodePermissions& perms) { out << perms.canConnectToDomain; out << perms.canAdjustLocks; diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 7971d4e362..e0713e5ce3 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -60,6 +60,9 @@ public: NodePermissions& operator|=(const NodePermissions& rhs); NodePermissions& operator|=(const NodePermissionsPointer& rhs); + NodePermissions& operator&=(const NodePermissions& rhs); + NodePermissions& operator&=(const NodePermissionsPointer& rhs); + NodePermissions operator~(); friend QDataStream& operator<<(QDataStream& out, const NodePermissions& perms); friend QDataStream& operator>>(QDataStream& in, NodePermissions& perms); @@ -93,5 +96,7 @@ const NodePermissions DEFAULT_AGENT_PERMISSIONS; QDebug operator<<(QDebug debug, const NodePermissions& perms); QDebug operator<<(QDebug debug, const NodePermissionsPointer& perms); NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs); +NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs); +NodePermissionsPointer operator~(NodePermissionsPointer& lhs); #endif // hifi_NodePermissions_h From d4cc4bf1e4d7c84efa7272e2ab06d0aa4ae7f8ed Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 27 Jun 2016 14:55:13 -0700 Subject: [PATCH 20/98] represent node permissions as a bitfield rather than a list of bools --- .../resources/describe-settings.json | 8 +- domain-server/src/DomainGatekeeper.cpp | 20 ++-- domain-server/src/DomainMetadata.cpp | 8 +- domain-server/src/DomainServer.cpp | 2 +- .../src/DomainServerSettingsManager.cpp | 25 +++-- libraries/networking/src/LimitedNodeList.cpp | 20 ++-- libraries/networking/src/LimitedNodeList.h | 8 +- libraries/networking/src/Node.h | 8 +- libraries/networking/src/NodePermissions.cpp | 97 ++++++++----------- libraries/networking/src/NodePermissions.h | 25 +++-- 10 files changed, 116 insertions(+), 105 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index b24acd1bce..44d2c025a7 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -409,7 +409,7 @@ "label": "Connect", "type": "checkbox", "editable": true, - "default": true + "default": false }, { "name": "id_can_adjust_locks", @@ -478,7 +478,7 @@ "label": "Connect", "type": "checkbox", "editable": true, - "default": true + "default": false }, { "name": "id_can_adjust_locks", @@ -549,7 +549,7 @@ "label": "Connect", "type": "checkbox", "editable": true, - "default": true + "default": false }, { "name": "id_can_adjust_locks", @@ -620,7 +620,7 @@ "label": "Connect", "type": "checkbox", "editable": true, - "default": true + "default": false }, { "name": "id_can_adjust_locks", diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index b5372ea9bc..9d121cb0ab 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -140,7 +140,7 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, } else { // they are logged into metaverse, but we don't have specific permissions for them. userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn); - qDebug() << "user-permissions: user is logged-into metavers, so:" << userPerms; + qDebug() << "user-permissions: user is logged-into metaverse, so:" << userPerms; // if this user is a friend of the domain-owner, give them friend's permissions if (_domainOwnerFriends.contains(verifiedUsername)) { @@ -188,9 +188,9 @@ void DomainGatekeeper::updateNodePermissions() { if (node->getPermissions().isAssignment) { // this node is an assignment-client userPerms.isAssignment = true; - userPerms.canAdjustLocks = true; - userPerms.canRezPermanentEntities = true; - userPerms.canRezTemporaryEntities = true; + userPerms.permissions |= NodePermissions::Permission::canAdjustLocks; + userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities; + userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities; } else { // this node is an agent const QHostAddress& addr = node->getLocalSocket().getAddress(); @@ -201,7 +201,7 @@ void DomainGatekeeper::updateNodePermissions() { node->setPermissions(userPerms); - if (!userPerms.canConnectToDomain) { + if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) { qDebug() << "node" << node->getUUID() << "no longer has permission to connect."; // hang up on this node nodesToKill << node; @@ -257,9 +257,9 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo // always allow assignment clients to create and destroy entities NodePermissions userPerms; userPerms.isAssignment = true; - userPerms.canAdjustLocks = true; - userPerms.canRezPermanentEntities = true; - userPerms.canRezTemporaryEntities = true; + userPerms.permissions |= NodePermissions::Permission::canAdjustLocks; + userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities; + userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities; newNode->setPermissions(userPerms); return newNode; } @@ -306,13 +306,13 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect userPerms = applyPermissionsForUser(isLocalUser, userPerms, verifiedUsername); - if (!userPerms.canConnectToDomain) { + if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) { sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.", nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::TooManyUsers); return SharedNodePointer(); } - if (!userPerms.canConnectPastMaxCapacity && !isWithinMaxCapacity()) { + if (!userPerms.can(NodePermissions::Permission::canConnectPastMaxCapacity) && !isWithinMaxCapacity()) { // we can't allow this user to connect because we are at max capacity sendConnectionDeniedPacket("Too many connected users.", nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::TooManyUsers); diff --git a/domain-server/src/DomainMetadata.cpp b/domain-server/src/DomainMetadata.cpp index c5048ea9d8..927a14cab1 100644 --- a/domain-server/src/DomainMetadata.cpp +++ b/domain-server/src/DomainMetadata.cpp @@ -184,10 +184,10 @@ void DomainMetadata::securityChanged(bool send) { QString restriction; const auto& settingsManager = static_cast(parent())->_settingsManager; - bool hasAnonymousAccess = - settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous).canConnectToDomain; - bool hasHifiAccess = - settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn).canConnectToDomain; + bool hasAnonymousAccess = settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous).can( + NodePermissions::Permission::canConnectToDomain); + bool hasHifiAccess = settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn).can( + NodePermissions::Permission::canConnectToDomain); if (hasAnonymousAccess) { restriction = hasHifiAccess ? RESTRICTION_OPEN : RESTRICTION_ANON; } else if (hasHifiAccess) { diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d75a2c3245..923a82d184 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1099,7 +1099,7 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) { // consider the domain to be "restricted" if anonymous connections are disallowed static const QString RESTRICTED_ACCESS_FLAG = "restricted"; NodePermissions anonymousPermissions = _settingsManager.getPermissionsForName(NodePermissions::standardNameAnonymous); - domainObject[RESTRICTED_ACCESS_FLAG] = !anonymousPermissions.canConnectToDomain; + domainObject[RESTRICTED_ACCESS_FLAG] = !anonymousPermissions.can(NodePermissions::Permission::canConnectToDomain); const auto& temporaryDomainKey = DependencyManager::get()->getTemporaryDomainKey(getID()); if (!temporaryDomainKey.isEmpty()) { diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 2411dbdf09..943f0517dc 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -223,14 +223,16 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList if (isRestrictedAccess) { // only users in allow-users list can connect - _standardAgentPermissions[NodePermissions::standardNameAnonymous]->canConnectToDomain = false; - _standardAgentPermissions[NodePermissions::standardNameLoggedIn]->canConnectToDomain = false; + _standardAgentPermissions[NodePermissions::standardNameAnonymous]->clear( + NodePermissions::Permission::canConnectToDomain); + _standardAgentPermissions[NodePermissions::standardNameLoggedIn]->clear( + NodePermissions::Permission::canConnectToDomain); } // else anonymous and logged-in retain default of canConnectToDomain = true foreach (QString allowedUser, allowedUsers) { // even if isRestrictedAccess is false, we have to add explicit rows for these users. - // defaults to canConnectToDomain = true _agentPermissions[allowedUser].reset(new NodePermissions(allowedUser)); + _agentPermissions[allowedUser]->set(NodePermissions::Permission::canConnectToDomain); } foreach (QString allowedEditor, allowedEditors) { @@ -238,10 +240,10 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList _agentPermissions[allowedEditor].reset(new NodePermissions(allowedEditor)); if (isRestrictedAccess) { // they can change locks, but can't connect. - _agentPermissions[allowedEditor]->canConnectToDomain = false; + _agentPermissions[allowedEditor]->clear(NodePermissions::Permission::canConnectToDomain); } } - _agentPermissions[allowedEditor]->canAdjustLocks = true; + _agentPermissions[allowedEditor]->set(NodePermissions::Permission::canAdjustLocks); } QList> permissionsSets; @@ -249,11 +251,16 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList foreach (auto permissionsSet, permissionsSets) { foreach (QString userName, permissionsSet.keys()) { if (onlyEditorsAreRezzers) { - permissionsSet[userName]->canRezPermanentEntities = permissionsSet[userName]->canAdjustLocks; - permissionsSet[userName]->canRezTemporaryEntities = permissionsSet[userName]->canAdjustLocks; + if (permissionsSet[userName]->can(NodePermissions::Permission::canAdjustLocks)) { + permissionsSet[userName]->set(NodePermissions::Permission::canRezPermanentEntities); + permissionsSet[userName]->set(NodePermissions::Permission::canRezTemporaryEntities); + } else { + permissionsSet[userName]->clear(NodePermissions::Permission::canRezPermanentEntities); + permissionsSet[userName]->clear(NodePermissions::Permission::canRezTemporaryEntities); + } } else { - permissionsSet[userName]->canRezPermanentEntities = true; - permissionsSet[userName]->canRezTemporaryEntities = true; + permissionsSet[userName]->set(NodePermissions::Permission::canRezPermanentEntities); + permissionsSet[userName]->set(NodePermissions::Permission::canRezTemporaryEntities); } } } diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index d7a2d47fab..151b6c5012 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -135,17 +135,21 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) { _permissions = newPermissions; - if (originalPermissions.canAdjustLocks != newPermissions.canAdjustLocks) { - emit isAllowedEditorChanged(_permissions.canAdjustLocks); + if (originalPermissions.can(NodePermissions::Permission::canAdjustLocks) != + newPermissions.can(NodePermissions::Permission::canAdjustLocks)) { + emit isAllowedEditorChanged(_permissions.can(NodePermissions::Permission::canAdjustLocks)); } - if (originalPermissions.canRezPermanentEntities != newPermissions.canRezPermanentEntities) { - emit canRezChanged(_permissions.canRezPermanentEntities); + if (originalPermissions.can(NodePermissions::Permission::canRezPermanentEntities) != + newPermissions.can(NodePermissions::Permission::canRezPermanentEntities)) { + emit canRezChanged(_permissions.can(NodePermissions::Permission::canRezPermanentEntities)); } - if (originalPermissions.canRezTemporaryEntities != newPermissions.canRezTemporaryEntities) { - emit canRezTmpChanged(_permissions.canRezTemporaryEntities); + if (originalPermissions.can(NodePermissions::Permission::canRezTemporaryEntities) != + newPermissions.can(NodePermissions::Permission::canRezTemporaryEntities)) { + emit canRezTmpChanged(_permissions.can(NodePermissions::Permission::canRezTemporaryEntities)); } - if (originalPermissions.canWriteToAssetServer != newPermissions.canWriteToAssetServer) { - emit canWriteAssetsChanged(_permissions.canWriteToAssetServer); + if (originalPermissions.can(NodePermissions::Permission::canWriteToAssetServer) != + newPermissions.can(NodePermissions::Permission::canWriteToAssetServer)) { + emit canWriteAssetsChanged(_permissions.can(NodePermissions::Permission::canWriteToAssetServer)); } } diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 483aa0734c..3242b7d178 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -105,10 +105,10 @@ public: void setSessionUUID(const QUuid& sessionUUID); void setPermissions(const NodePermissions& newPermissions); - bool isAllowedEditor() const { return _permissions.canAdjustLocks; } - bool getThisNodeCanRez() const { return _permissions.canRezPermanentEntities; } - bool getThisNodeCanRezTmp() const { return _permissions.canRezTemporaryEntities; } - bool getThisNodeCanWriteAssets() const { return _permissions.canWriteToAssetServer; } + bool isAllowedEditor() const { return _permissions.can(NodePermissions::Permission::canAdjustLocks); } + bool getThisNodeCanRez() const { return _permissions.can(NodePermissions::Permission::canRezPermanentEntities); } + bool getThisNodeCanRezTmp() const { return _permissions.can(NodePermissions::Permission::canRezTemporaryEntities); } + bool getThisNodeCanWriteAssets() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); } quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); } QUdpSocket& getDTLSSocket(); diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index b277ac0083..08eb9829f8 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -61,10 +61,10 @@ public: void setPermissions(const NodePermissions& newPermissions) { _permissions = newPermissions; } NodePermissions getPermissions() const { return _permissions; } - bool isAllowedEditor() const { return _permissions.canAdjustLocks; } - bool getCanRez() const { return _permissions.canRezPermanentEntities; } - bool getCanRezTmp() const { return _permissions.canRezTemporaryEntities; } - bool getCanWriteToAssetServer() const { return _permissions.canWriteToAssetServer; } + bool isAllowedEditor() const { return _permissions.can(NodePermissions::Permission::canAdjustLocks); } + bool getCanRez() const { return _permissions.can(NodePermissions::Permission::canRezPermanentEntities); } + bool getCanRezTmp() const { return _permissions.can(NodePermissions::Permission::canRezTemporaryEntities); } + bool getCanWriteToAssetServer() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); } friend QDataStream& operator<<(QDataStream& out, const Node& node); friend QDataStream& operator>>(QDataStream& in, Node& node); diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index a6e502ff59..023e3cb04a 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -31,12 +31,14 @@ NodePermissions::NodePermissions(QMap perms) { _groupID = perms["group_id"].toUuid(); } - 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(); + permissions = NodePermissions::Permissions(); + permissions |= perms["id_can_connect"].toBool() ? Permission::canConnectToDomain : Permission::none; + permissions |= perms["id_can_adjust_locks"].toBool() ? Permission::canAdjustLocks : Permission::none; + permissions |= perms["id_can_rez"].toBool() ? Permission::canRezPermanentEntities : Permission::none; + permissions |= perms["id_can_rez_tmp"].toBool() ? Permission::canRezTemporaryEntities : Permission::none; + permissions |= perms["id_can_write_to_asset_server"].toBool() ? Permission::canWriteToAssetServer : Permission::none; + permissions |= perms["id_can_connect_past_max_capacity"].toBool() ? + Permission::canConnectPastMaxCapacity : Permission::none; } QVariant NodePermissions::toVariant() { @@ -45,31 +47,24 @@ QVariant NodePermissions::toVariant() { if (_groupIDSet) { values["group_id"] = _groupID; } - 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; + values["id_can_connect"] = can(Permission::canConnectToDomain); + values["id_can_adjust_locks"] = can(Permission::canAdjustLocks); + values["id_can_rez"] = can(Permission::canRezPermanentEntities); + values["id_can_rez_tmp"] = can(Permission::canRezTemporaryEntities); + values["id_can_write_to_asset_server"] = can(Permission::canWriteToAssetServer); + values["id_can_connect_past_max_capacity"] = can(Permission::canConnectPastMaxCapacity); return QVariant(values); } void NodePermissions::setAll(bool value) { - canConnectToDomain = value; - canAdjustLocks = value; - canRezPermanentEntities = value; - canRezTemporaryEntities = value; - canWriteToAssetServer = value; - canConnectPastMaxCapacity = value; + permissions = NodePermissions::Permissions(); + if (value) { + permissions = ~permissions; + } } NodePermissions& NodePermissions::operator|=(const NodePermissions& 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; + permissions |= rhs.permissions; return *this; } NodePermissions& NodePermissions::operator|=(const NodePermissionsPointer& rhs) { @@ -84,14 +79,15 @@ NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, const NodePermis } return lhs; } +NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, NodePermissions::Permission rhs) { + if (lhs) { + lhs.get()->permissions |= rhs; + } + return lhs; +} NodePermissions& NodePermissions::operator&=(const NodePermissions& 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; + permissions &= rhs.permissions; return *this; } NodePermissions& NodePermissions::operator&=(const NodePermissionsPointer& rhs) { @@ -106,15 +102,16 @@ NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, const NodePermis } return lhs; } +NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, NodePermissions::Permission rhs) { + if (lhs) { + lhs.get()->permissions &= rhs; + } + return lhs; +} NodePermissions NodePermissions::operator~() { NodePermissions result = *this; - result.canConnectToDomain = !result.canConnectToDomain; - result.canAdjustLocks = !result.canAdjustLocks; - result.canRezPermanentEntities = !result.canRezPermanentEntities; - result.canRezTemporaryEntities = !result.canRezTemporaryEntities; - result.canWriteToAssetServer = !result.canWriteToAssetServer; - result.canConnectPastMaxCapacity = !result.canConnectPastMaxCapacity; + result.permissions = ~permissions; return result; } @@ -128,43 +125,35 @@ NodePermissionsPointer operator~(NodePermissionsPointer& lhs) { } QDataStream& operator<<(QDataStream& out, const NodePermissions& perms) { - out << perms.canConnectToDomain; - out << perms.canAdjustLocks; - out << perms.canRezPermanentEntities; - out << perms.canRezTemporaryEntities; - out << perms.canWriteToAssetServer; - out << perms.canConnectPastMaxCapacity; + out << (uint)perms.permissions; return out; } QDataStream& operator>>(QDataStream& in, NodePermissions& perms) { - in >> perms.canConnectToDomain; - in >> perms.canAdjustLocks; - in >> perms.canRezPermanentEntities; - in >> perms.canRezTemporaryEntities; - in >> perms.canWriteToAssetServer; - in >> perms.canConnectPastMaxCapacity; + uint permissionsInt; + in >> permissionsInt; + perms.permissions = (NodePermissions::Permissions)permissionsInt; return in; } QDebug operator<<(QDebug debug, const NodePermissions& perms) { debug.nospace() << "[permissions: " << perms.getID() << " --"; - if (perms.canConnectToDomain) { + if (perms.can(NodePermissions::Permission::canConnectToDomain)) { debug << " connect"; } - if (perms.canAdjustLocks) { + if (perms.can(NodePermissions::Permission::canAdjustLocks)) { debug << " locks"; } - if (perms.canRezPermanentEntities) { + if (perms.can(NodePermissions::Permission::canRezPermanentEntities)) { debug << " rez"; } - if (perms.canRezTemporaryEntities) { + if (perms.can(NodePermissions::Permission::canRezTemporaryEntities)) { debug << " rez-tmp"; } - if (perms.canWriteToAssetServer) { + if (perms.can(NodePermissions::Permission::canWriteToAssetServer)) { debug << " asset-server"; } - if (perms.canConnectPastMaxCapacity) { + if (perms.can(NodePermissions::Permission::canConnectPastMaxCapacity)) { debug << " ignore-max-cap"; } debug.nospace() << "]"; diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index e0713e5ce3..1dea560543 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -46,13 +46,17 @@ public: static QString standardNameFriends; static QStringList standardNames; - // 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 }; + enum class Permission { + none = 0, + canConnectToDomain = 1, + canAdjustLocks = 2, + canRezPermanentEntities = 4, + canRezTemporaryEntities = 8, + canWriteToAssetServer = 16, + canConnectPastMaxCapacity = 32 + }; + Q_DECLARE_FLAGS(Permissions, Permission) + Permissions permissions; QVariant toVariant(); @@ -66,6 +70,10 @@ public: friend QDataStream& operator<<(QDataStream& out, const NodePermissions& perms); friend QDataStream& operator>>(QDataStream& in, NodePermissions& perms); + void clear(Permission p) { permissions &= (Permission) (~(uint)p); } + void set(Permission p) { permissions |= p; } + bool can(Permission p) const { return permissions.testFlag(p); } + protected: QString _id; QString _userName; @@ -73,6 +81,7 @@ protected: bool _groupIDSet { false }; QUuid _groupID; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(NodePermissions::Permissions) // wrap QHash in a class that forces all keys to be lowercase @@ -96,7 +105,9 @@ const NodePermissions DEFAULT_AGENT_PERMISSIONS; QDebug operator<<(QDebug debug, const NodePermissions& perms); QDebug operator<<(QDebug debug, const NodePermissionsPointer& perms); NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs); +NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, NodePermissions::Permission rhs); NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs); +NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, NodePermissions::Permission rhs); NodePermissionsPointer operator~(NodePermissionsPointer& lhs); #endif // hifi_NodePermissions_h From aae42e3802574398947457e438571ea9b273b84b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 30 Jun 2016 16:14:46 -0700 Subject: [PATCH 21/98] updates to track zach's changes to api --- domain-server/src/DomainGatekeeper.cpp | 31 +++++-- domain-server/src/DomainServer.cpp | 2 + .../src/DomainServerSettingsManager.cpp | 86 +++++++++++++------ .../src/DomainServerSettingsManager.h | 1 + libraries/networking/src/AccountManager.cpp | 2 + 5 files changed, 92 insertions(+), 30 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 9d121cb0ab..77cd7ffb94 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -711,20 +711,39 @@ void DomainGatekeeper::getIsGroupMember(const QString& username, const QUuid gro callbackParams.errorCallbackReceiver = this; callbackParams.errorCallbackMethod = "getIsGroupMemberErrorCallback"; - const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/%1/is_member/%2"; + const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/%1/membership/%2"; QString groupIDStr = groupID.toString().mid(1,36); DependencyManager::get()->sendRequest(GET_IS_GROUP_MEMBER_PATH.arg(groupIDStr).arg(username), - AccountManagerAuth::None, + AccountManagerAuth::Required, QNetworkAccessManager::GetOperation, callbackParams); } void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) { + // { + // "data":{ + // "username":"sethalves", + // "groups":{ + // "fd55479a-265d-4990-854e-3d04214ad1b0":{ + // "name":"Blerg Blah", + // "rank":{ + // "name":"admin", + // "order":1 + // } + // } + // } + // }, + // "status":"success" + // } + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); if (jsonObject["status"].toString() == "success") { - QString username = jsonObject["username"].toString(); - QUuid groupID = QUuid(jsonObject["group_id"].toString()); - bool isMember = jsonObject["is_member"].toBool(); - _server->_settingsManager.recordGroupMembership(username, groupID, isMember); + QJsonObject data = jsonObject["data"].toObject(); + QJsonObject groups = data["groups"].toObject(); + QString username = data["username"].toString(); + _server->_settingsManager.clearGroupMemberships(username); + foreach (auto groupID, groups.keys()) { + _server->_settingsManager.recordGroupMembership(username, groupID, true); + } } else { qDebug() << "getIsGroupMember api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d78199ba8e..fcbf4caa5b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -112,6 +112,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : return; } + _settingsManager.requestMissingGroupIDs(); + setupNodeListAndAssignments(); setupAutomaticNetworking(); if (!getID().isNull()) { diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 56656f9d7c..daea38de23 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -1004,6 +1004,11 @@ void DomainServerSettingsManager::persistToFile() { } void DomainServerSettingsManager::requestMissingGroupIDs() { + if (!DependencyManager::get()->hasAuthEndpoint()) { + // can't yet. + return; + } + QHashIterator i_permissions(_groupPermissions.get()); while (i_permissions.hasNext()) { i_permissions.next(); @@ -1044,40 +1049,73 @@ void DomainServerSettingsManager::getGroupID(const QString& groupname) { callbackParams.errorCallbackReceiver = this; callbackParams.errorCallbackMethod = "getGroupIDErrorCallback"; - const QString GET_GROUP_ID_PATH = "api/v1/get_group_id/%1"; + const QString GET_GROUP_ID_PATH = "api/v1/groups/name/%1"; - qDebug() << "Requesting group ID for group named" << groupname; + qDebug() << "************* Requesting group ID for group named" << groupname; DependencyManager::get()->sendRequest(GET_GROUP_ID_PATH.arg(groupname), - AccountManagerAuth::None, + AccountManagerAuth::Required, QNetworkAccessManager::GetOperation, callbackParams); } void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestReply) { + // { + // "data":{ + // "groups":[{ + // "description":null, + // "id":"fd55479a-265d-4990-854e-3d04214ad1b0", + // "is_list":false, + // "membership":{ + // "permissions":{ + // "custom_1=":false, + // "custom_2=":false, + // "custom_3=":false, + // "custom_4=":false, + // "del_group=":true, + // "invite_member=":true, + // "kick_member=":true, + // "list_members=":true, + // "mv_group=":true, + // "query_members=":true, + // "rank_member=":true + // }, + // "rank":{ + // "name=":"owner", + // "order=":0 + // } + // }, + // "name":"Blerg Blah" + // }] + // }, + // "status":"success" + // } QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); - if (jsonObject["status"].toString() == "success") { - QString groupName = jsonObject["group_name"].toString(); - QUuid groupID = QUuid(jsonObject["group_id"].toString()); + QJsonArray groups = jsonObject["data"].toObject()["groups"].toArray(); + for (int i = 0; i < groups.size(); i++) { + QJsonObject group = groups.at(i).toObject(); + QString groupName = group["name"].toString(); + QUuid groupID = QUuid(group["id"].toString()); - bool found = false; - if (_groupPermissions.contains(groupName)) { - qDebug() << "ID for group:" << groupName << "is" << groupID; - _groupPermissions[groupName]->setGroupID(groupID); - _groupByID[groupID] = _groupPermissions[groupName]; - found = true; - } - if (_groupForbiddens.contains(groupName)) { - qDebug() << "ID for group:" << groupName << "is" << groupID; - _groupForbiddens[groupName]->setGroupID(groupID); - _groupByID[groupID] = _groupForbiddens[groupName]; - found = true; - } + bool found = false; + if (_groupPermissions.contains(groupName)) { + qDebug() << "ID for group:" << groupName << "is" << groupID; + _groupPermissions[groupName]->setGroupID(groupID); + _groupByID[groupID] = _groupPermissions[groupName]; + found = true; + } + if (_groupForbiddens.contains(groupName)) { + qDebug() << "ID for group:" << groupName << "is" << groupID; + _groupForbiddens[groupName]->setGroupID(groupID); + _groupByID[groupID] = _groupForbiddens[groupName]; + found = true; + } - if (found) { - packPermissions(); - } else { - qDebug() << "DomainServerSettingsManager::getGroupIDJSONCallback got response for unknown group:" << groupName; + if (found) { + packPermissions(); + } else { + qDebug() << "DomainServerSettingsManager::getGroupIDJSONCallback got response for unknown group:" << groupName; + } } } else { qDebug() << "getGroupID api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); @@ -1085,7 +1123,7 @@ void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestR } void DomainServerSettingsManager::getGroupIDErrorCallback(QNetworkReply& requestReply) { - qDebug() << "getGroupID api call failed:" << requestReply.error(); + qDebug() << "******************** getGroupID api call failed:" << requestReply.error(); } void DomainServerSettingsManager::recordGroupMembership(const QString& name, const QUuid groupID, bool isMember) { diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index a4978c8e91..c343a6bc06 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -68,6 +68,7 @@ public: QList getBlacklistGroupIDs(); // these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api + void clearGroupMemberships(const QString& name) { _groupMembership[name].clear(); } void recordGroupMembership(const QString& name, const QUuid groupID, bool isMember); bool isGroupMember(const QString& name, const QUuid& groupID); diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 89e6a6ea10..571f54322b 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -253,6 +253,8 @@ void AccountManager::sendRequest(const QString& path, } } + qDebug() << "/////// path =" << path << " requestURL =" << requestURL; + networkRequest.setUrl(requestURL); if (VERBOSE_HTTP_REQUEST_DEBUGGING) { From 2a25f458fc03399c88b48b31f4932b525ee4ed3a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 4 Jul 2016 10:20:25 -0700 Subject: [PATCH 22/98] fetch rank names from web api after getting a group's ID --- .../src/DomainServerSettingsManager.cpp | 96 +++++++++++++++++++ .../src/DomainServerSettingsManager.h | 6 ++ libraries/networking/src/AccountManager.cpp | 2 - 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index daea38de23..5c27f37172 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -1090,6 +1090,7 @@ void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestR // "status":"success" // } QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + if (jsonObject["status"].toString() == "success") { QJsonArray groups = jsonObject["data"].toObject()["groups"].toArray(); for (int i = 0; i < groups.size(); i++) { @@ -1113,6 +1114,7 @@ void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestR if (found) { packPermissions(); + getGroupRanks(groupID); } else { qDebug() << "DomainServerSettingsManager::getGroupIDJSONCallback got response for unknown group:" << groupName; } @@ -1126,6 +1128,100 @@ void DomainServerSettingsManager::getGroupIDErrorCallback(QNetworkReply& request qDebug() << "******************** getGroupID api call failed:" << requestReply.error(); } +void DomainServerSettingsManager::getGroupRanks(const QUuid& groupID) { + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "getGroupRanksJSONCallback"; + callbackParams.errorCallbackReceiver = this; + callbackParams.errorCallbackMethod = "getGroupRanksErrorCallback"; + + const QString GET_GROUP_RANKS_PATH = "api/v1/groups/%1/ranks"; + + qDebug() << "************* Requesting group ranks for group" << groupID; + + DependencyManager::get()->sendRequest(GET_GROUP_RANKS_PATH.arg(groupID.toString().mid(1,36)), + AccountManagerAuth::Required, + QNetworkAccessManager::GetOperation, callbackParams); +} + +void DomainServerSettingsManager::getGroupRanksJSONCallback(QNetworkReply& requestReply) { + // { + // "current_page":1, + // "data":{ + // "groups":{ + // "fd55479a-265d-4990-854e-3d04214ad1b0":{ + // "ranks":[ + // { + // "name":"owner", + // "order":0, + // "permissions":{ + // "custom_1":false, + // "custom_2":false, + // "custom_3":false, + // "custom_4":false, + // "del_group":true, + // "invite_member":true, + // "kick_member":true, + // "list_members":true, + // "mv_group":true, + // "query_members":true, + // "rank_member":true + // } + // }, + // { + // "name":"admin", + // "order":1, + // "permissions":{ + // "custom_1":false, + // "custom_2":false, + // "custom_3":false, + // "custom_4":false, + // "del_group":false, + // "invite_member":false, + // "kick_member":false, + // "list_members":false, + // "mv_group":false, + // "query_members":false, + // "rank_member":false + // } + // } + // ] + // } + // } + // }, + // "per_page":30, + // "status":"success", + // "total_entries":2, + // "total_pages":1 + // } + + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + if (jsonObject["status"].toString() == "success") { + QJsonObject groups = jsonObject["data"].toObject()["groups"].toObject(); + foreach (auto groupID, groups.keys()) { + QJsonObject group = groups[groupID].toObject(); + QJsonArray ranks = group["ranks"].toArray(); + for (int rankIndex = 0; rankIndex < ranks.size(); rankIndex++) { + QJsonObject rank = ranks.at(rankIndex).toObject(); + QString rankName = rank["name"].toString(); + int rankOrder = rank["order"].toInt(); + qDebug() << "**** " << groupID << "rank name =" << rankName << " order =" << rankOrder; + QVector& ranksForGroup = _groupRanks[groupID]; + if (ranksForGroup.size() < rankOrder + 1) { + ranksForGroup.resize(rankOrder + 1); + } + ranksForGroup[rankOrder] = rankName; + } + } + } else { + qDebug() << "getGroupRanks api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); + } +} + +void DomainServerSettingsManager::getGroupRanksErrorCallback(QNetworkReply& requestReply) { + qDebug() << "******************** getGroupRanks api call failed:" << requestReply.error(); +} + void DomainServerSettingsManager::recordGroupMembership(const QString& name, const QUuid groupID, bool isMember) { _groupMembership[name][groupID] = isMember; } diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index c343a6bc06..706717eda8 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -78,6 +78,8 @@ signals: public slots: void getGroupIDJSONCallback(QNetworkReply& requestReply); void getGroupIDErrorCallback(QNetworkReply& requestReply); + void getGroupRanksJSONCallback(QNetworkReply& requestReply); + void getGroupRanksErrorCallback(QNetworkReply& requestReply); private slots: void processSettingsRequestPacket(QSharedPointer message); @@ -106,6 +108,7 @@ private: void requestMissingGroupIDs(); void getGroupID(const QString& groupname); NodePermissionsPointer lookupGroupByID(const QUuid& id); + void getGroupRanks(const QUuid& groupID); void packPermissionsForMap(QString mapName, NodePermissionsMap& agentPermissions, QString keyPath); void packPermissions(); @@ -117,6 +120,9 @@ private: NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group QHash _groupByID; // similar to _groupPermissions but key is group-id rather than name + // remember the responses to api/v1/groups/%1/ranks + QHash> _groupRanks; + // keep track of answers to api queries about which users are in which groups QHash> _groupMembership; }; diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 34fab3575f..c4bfae7cac 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -249,8 +249,6 @@ void AccountManager::sendRequest(const QString& path, } } - qDebug() << "/////// path =" << path << " requestURL =" << requestURL; - networkRequest.setUrl(requestURL); if (VERBOSE_HTTP_REQUEST_DEBUGGING) { From e6f456f49468fd41f1230236beb0c7abe9022c2a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 6 Jul 2016 16:39:08 -0700 Subject: [PATCH 23/98] each rank in a group has its own permissions --- .../resources/describe-settings.json | 20 +- domain-server/src/DomainGatekeeper.cpp | 19 +- domain-server/src/DomainServer.cpp | 3 +- .../src/DomainServerSettingsManager.cpp | 443 ++++++++++++------ .../src/DomainServerSettingsManager.h | 66 ++- libraries/networking/src/NodePermissions.cpp | 35 +- libraries/networking/src/NodePermissions.h | 57 ++- 7 files changed, 429 insertions(+), 214 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 44d2c025a7..885a289db4 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -526,7 +526,7 @@ "groups": [ { "label": "Group", - "span": 2 + "span": 4 }, { "label": "Permissions ?", @@ -539,6 +539,14 @@ "name": "permissions_id", "label": "Group Name" }, + { + "name": "rank", + "label": "Rank" + }, + { + "name": "rank_name", + "label": "Rank Name" + }, { "name": "group_id", "label": "Group ID", @@ -597,7 +605,7 @@ "groups": [ { "label": "Group", - "span": 2 + "span": 4 }, { "label": "Permissions ?", @@ -610,6 +618,14 @@ "name": "permissions_id", "label": "Group Name" }, + { + "name": "rank", + "label": "Rank" + }, + { + "name": "rank_name", + "label": "Rank Name" + }, { "name": "group_id", "label": "Group ID", diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 77cd7ffb94..f85766105e 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -150,8 +150,9 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, // if this user is a known member of a group, give them the implied permissions foreach (QUuid groupID, _server->_settingsManager.getGroupIDs()) { - if (_server->_settingsManager.isGroupMember(verifiedUsername, groupID)) { - userPerms |= _server->_settingsManager.getPermissionsForGroup(groupID); + int rank = _server->_settingsManager.isGroupMember(verifiedUsername, groupID); + if (rank >= 0) { + userPerms |= _server->_settingsManager.getPermissionsForGroup(groupID, rank); qDebug() << "user-permissions: user is in group:" << groupID << "so:" << userPerms; } } @@ -159,11 +160,13 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, // if this user is a known member of a blacklist group, remove the implied permissions foreach (QUuid groupID, _server->_settingsManager.getBlacklistGroupIDs()) { if (_server->_settingsManager.isGroupMember(verifiedUsername, groupID)) { - userPerms &= ~_server->_settingsManager.getForbiddensForGroup(groupID); - qDebug() << "user-permissions: user is in blacklist group:" << groupID << "so:" << userPerms; + int rank = _server->_settingsManager.isGroupMember(verifiedUsername, groupID); + if (rank >= 0) { + userPerms &= ~_server->_settingsManager.getForbiddensForGroup(groupID, rank); + qDebug() << "user-permissions: user is in blacklist group:" << groupID << "so:" << userPerms; + } } } - } } @@ -183,7 +186,7 @@ void DomainGatekeeper::updateNodePermissions() { // the id and the username in NodePermissions will often be the same, but id is set before // authentication and username is only set once they user's key has been confirmed. QString username = node->getPermissions().getUserName(); - NodePermissions userPerms(username); + NodePermissions userPerms(NodePermissionsKey(username, 0)); if (node->getPermissions().isAssignment) { // this node is an assignment-client @@ -273,7 +276,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect auto limitedNodeList = DependencyManager::get(); // start with empty permissions - NodePermissions userPerms(username); + NodePermissions userPerms(NodePermissionsKey(username, 0)); userPerms.setAll(false); // check if this user is on our local machine - if this is true set permissions to those for a "localhost" connection @@ -711,7 +714,7 @@ void DomainGatekeeper::getIsGroupMember(const QString& username, const QUuid gro callbackParams.errorCallbackReceiver = this; callbackParams.errorCallbackMethod = "getIsGroupMemberErrorCallback"; - const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/%1/membership/%2"; + const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/%1/members/%2"; QString groupIDStr = groupID.toString().mid(1,36); DependencyManager::get()->sendRequest(GET_IS_GROUP_MEMBER_PATH.arg(groupIDStr).arg(username), AccountManagerAuth::Required, diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index fcbf4caa5b..d5bf8d9adc 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -112,7 +112,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : return; } - _settingsManager.requestMissingGroupIDs(); + _settingsManager.apiRefreshGroupInformation(); setupNodeListAndAssignments(); setupAutomaticNetworking(); @@ -1097,7 +1097,6 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) { static const QString AUTOMATIC_NETWORKING_KEY = "automatic_networking"; domainObject[AUTOMATIC_NETWORKING_KEY] = _automaticNetworkingSetting; - // add access level for anonymous connections // consider the domain to be "restricted" if anonymous connections are disallowed static const QString RESTRICTED_ACCESS_FLAG = "restricted"; diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 5c27f37172..3f44b9a293 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -232,36 +232,37 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList foreach (QString allowedUser, allowedUsers) { // even if isRestrictedAccess is false, we have to add explicit rows for these users. - _agentPermissions[allowedUser].reset(new NodePermissions(allowedUser)); - _agentPermissions[allowedUser]->set(NodePermissions::Permission::canConnectToDomain); + _agentPermissions[NodePermissionsKey(allowedUser, 0)].reset(new NodePermissions(allowedUser)); + _agentPermissions[NodePermissionsKey(allowedUser, 0)]->set(NodePermissions::Permission::canConnectToDomain); } foreach (QString allowedEditor, allowedEditors) { - if (!_agentPermissions.contains(allowedEditor)) { - _agentPermissions[allowedEditor].reset(new NodePermissions(allowedEditor)); + NodePermissionsKey editorKey(allowedEditor, 0); + if (!_agentPermissions.contains(editorKey)) { + _agentPermissions[editorKey].reset(new NodePermissions(allowedEditor)); if (isRestrictedAccess) { // they can change locks, but can't connect. - _agentPermissions[allowedEditor]->clear(NodePermissions::Permission::canConnectToDomain); + _agentPermissions[editorKey]->clear(NodePermissions::Permission::canConnectToDomain); } } - _agentPermissions[allowedEditor]->set(NodePermissions::Permission::canAdjustLocks); + _agentPermissions[editorKey]->set(NodePermissions::Permission::canAdjustLocks); } - QList> permissionsSets; + QList> permissionsSets; permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get(); foreach (auto permissionsSet, permissionsSets) { - foreach (QString userName, permissionsSet.keys()) { + foreach (NodePermissionsKey userKey, permissionsSet.keys()) { if (onlyEditorsAreRezzers) { - if (permissionsSet[userName]->can(NodePermissions::Permission::canAdjustLocks)) { - permissionsSet[userName]->set(NodePermissions::Permission::canRezPermanentEntities); - permissionsSet[userName]->set(NodePermissions::Permission::canRezTemporaryEntities); + if (permissionsSet[userKey]->can(NodePermissions::Permission::canAdjustLocks)) { + permissionsSet[userKey]->set(NodePermissions::Permission::canRezPermanentEntities); + permissionsSet[userKey]->set(NodePermissions::Permission::canRezTemporaryEntities); } else { - permissionsSet[userName]->clear(NodePermissions::Permission::canRezPermanentEntities); - permissionsSet[userName]->clear(NodePermissions::Permission::canRezTemporaryEntities); + permissionsSet[userKey]->clear(NodePermissions::Permission::canRezPermanentEntities); + permissionsSet[userKey]->clear(NodePermissions::Permission::canRezTemporaryEntities); } } else { - permissionsSet[userName]->set(NodePermissions::Permission::canRezPermanentEntities); - permissionsSet[userName]->set(NodePermissions::Permission::canRezTemporaryEntities); + permissionsSet[userKey]->set(NodePermissions::Permission::canRezPermanentEntities); + permissionsSet[userKey]->set(NodePermissions::Permission::canRezTemporaryEntities); } } } @@ -343,11 +344,17 @@ void DomainServerSettingsManager::packPermissionsForMap(QString mapName, (*permissions) = QVariantList(); } - // convert details for each member of the section + // convert details for each member of the subsection QVariantList* permissionsList = reinterpret_cast(permissions); (*permissionsList).clear(); - foreach (QString userName, agentPermissions.keys()) { - *permissionsList += agentPermissions[userName]->toVariant(); + foreach (NodePermissionsKey userKey, agentPermissions.keys()) { + NodePermissionsPointer perms = agentPermissions[userKey]; + if (perms->isGroup()) { + QVector rankNames = _groupRanks[perms->getGroupID()]; + *permissionsList += perms->toVariant(rankNames); + } else { + *permissionsList += perms->toVariant(); + } } } @@ -413,16 +420,17 @@ void DomainServerSettingsManager::unpackPermissions() { foreach (QVariant permsHash, standardPermissionsList) { NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; QString id = perms->getID(); - foundLocalhost |= (id == NodePermissions::standardNameLocalhost); - foundAnonymous |= (id == NodePermissions::standardNameAnonymous); - foundLoggedIn |= (id == NodePermissions::standardNameLoggedIn); - foundFriends |= (id == NodePermissions::standardNameFriends); - if (_standardAgentPermissions.contains(id)) { + NodePermissionsKey idKey = NodePermissionsKey(id, 0); + foundLocalhost |= (idKey == NodePermissions::standardNameLocalhost); + foundAnonymous |= (idKey == NodePermissions::standardNameAnonymous); + foundLoggedIn |= (idKey == NodePermissions::standardNameLoggedIn); + foundFriends |= (idKey == NodePermissions::standardNameFriends); + if (_standardAgentPermissions.contains(idKey)) { qDebug() << "duplicate name in standard permissions table: " << id; - _standardAgentPermissions[id] |= perms; + _standardAgentPermissions[idKey] |= perms; needPack = true; } else { - _standardAgentPermissions[id] = perms; + _standardAgentPermissions[idKey] = perms; } } @@ -430,12 +438,13 @@ void DomainServerSettingsManager::unpackPermissions() { foreach (QVariant permsHash, permissionsList) { NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; QString id = perms->getID(); - if (_agentPermissions.contains(id)) { + NodePermissionsKey idKey = NodePermissionsKey(id, 0); + if (_agentPermissions.contains(idKey)) { qDebug() << "duplicate name in permissions table: " << id; - _agentPermissions[id] |= perms; + _agentPermissions[idKey] |= perms; needPack = true; } else { - _agentPermissions[id] = perms; + _agentPermissions[idKey] = perms; } } @@ -443,16 +452,18 @@ void DomainServerSettingsManager::unpackPermissions() { foreach (QVariant permsHash, groupPermissionsList) { NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; QString id = perms->getID(); - if (_groupPermissions.contains(id)) { + NodePermissionsKey idKey = perms->getKey(); + if (_groupPermissions.contains(idKey)) { qDebug() << "duplicate name in group permissions table: " << id; - _groupPermissions[id] |= perms; + _groupPermissions[idKey] |= perms; needPack = true; } else { - _groupPermissions[id] = perms; + _groupPermissions[idKey] = perms; } if (perms->isGroup()) { - // the group-id was cached. hook-up the id in the id->group hash - _groupByID[perms->getGroupID()] = _groupPermissions[id]; + // the group-id was cached. hook-up the uuid in the uuid->group hash + _groupPermissionsByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRank())] = _groupPermissions[idKey]; + needPack |= setGroupID(perms->getID(), perms->getGroupID()); } } @@ -460,16 +471,18 @@ void DomainServerSettingsManager::unpackPermissions() { foreach (QVariant permsHash, groupForbiddensList) { NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; QString id = perms->getID(); - if (_groupForbiddens.contains(id)) { + NodePermissionsKey idKey = perms->getKey(); + if (_groupForbiddens.contains(idKey)) { qDebug() << "duplicate name in group forbiddens table: " << id; - _groupForbiddens[id] |= perms; + _groupForbiddens[idKey] |= perms; needPack = true; } else { - _groupForbiddens[id] = perms; + _groupForbiddens[idKey] = perms; } if (perms->isGroup()) { - // the group-id was cached. hook-up the id in the id->group hash - _groupByID[perms->getGroupID()] = _groupForbiddens[id]; + // the group-id was cached. hook-up the uuid in the uuid->group hash + _groupForbiddensByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRank())] = _groupPermissions[idKey]; + needPack |= setGroupID(perms->getID(), perms->getGroupID()); } } @@ -477,40 +490,42 @@ void DomainServerSettingsManager::unpackPermissions() { if (!foundLocalhost) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLocalhost) }; perms->setAll(true); - _standardAgentPermissions[perms->getID()] = perms; + _standardAgentPermissions[perms->getKey()] = perms; needPack = true; } if (!foundAnonymous) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameAnonymous) }; - _standardAgentPermissions[perms->getID()] = perms; + _standardAgentPermissions[perms->getKey()] = perms; needPack = true; } if (!foundLoggedIn) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLoggedIn) }; - _standardAgentPermissions[perms->getID()] = perms; + _standardAgentPermissions[perms->getKey()] = perms; needPack = true; } if (!foundFriends) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameFriends) }; - _standardAgentPermissions[perms->getID()] = perms; + _standardAgentPermissions[perms->getKey()] = perms; needPack = true; } + needPack |= ensurePermissionsForGroupRanks(); + if (needPack) { packPermissions(); } - // attempt to retrieve any missing group-IDs - requestMissingGroupIDs(); + // attempt to retrieve any missing group-IDs, etc + apiRefreshGroupInformation(); #ifdef WANT_DEBUG qDebug() << "--------------- permissions ---------------------"; - QList> permissionsSets; + QList> permissionsSets; permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get() << _groupPermissions.get() << _groupForbiddens.get(); foreach (auto permissionSet, permissionsSets) { - QHashIterator i(permissionSet); + QHashIterator i(permissionSet); while (i.hasNext()) { i.next(); NodePermissionsPointer perms = i.value(); @@ -524,7 +539,63 @@ void DomainServerSettingsManager::unpackPermissions() { #endif } -NodePermissions DomainServerSettingsManager::getStandardPermissionsForName(const QString& name) const { +bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() { + // make sure each rank in each group has its own set of permissions + bool changed = false; + QList permissionGroupIDs = getGroupIDs(); + foreach (QUuid groupID, permissionGroupIDs) { + QString groupName = _groupNames[groupID]; + int rankCountForGroup = _groupRanks[groupID].size(); + for (int rank = 0; rank < rankCountForGroup; rank++) { + NodePermissionsKey nameKey = NodePermissionsKey(groupName, rank); + GroupByUUIDKey idKey = GroupByUUIDKey(groupID, rank); + NodePermissionsPointer perms; + if (_groupPermissions.contains(nameKey)) { + perms = _groupPermissions[nameKey]; + } else { + perms = NodePermissionsPointer(new NodePermissions(nameKey)); + perms->setGroupID(groupID); + _groupPermissions[nameKey] = perms; + changed = true; + } + _groupPermissionsByUUID[idKey] = perms; + } + } + + QList forbiddenGroupIDs = getBlacklistGroupIDs(); + foreach (QUuid groupID, forbiddenGroupIDs) { + QString groupName = _groupNames[groupID]; + int rankCountForGroup = _groupRanks[groupID].size(); + for (int rank = 0; rank < rankCountForGroup; rank++) { + NodePermissionsKey nameKey = NodePermissionsKey(groupName, rank); + GroupByUUIDKey idKey = GroupByUUIDKey(groupID, rank); + NodePermissionsPointer perms; + if (_groupForbiddens.contains(nameKey)) { + perms = _groupForbiddens[nameKey]; + } else { + perms = NodePermissionsPointer(new NodePermissions(nameKey)); + perms->setGroupID(groupID); + _groupForbiddens[nameKey] = perms; + changed = true; + } + _groupForbiddensByUUID[idKey] = perms; + } + } + + debugDumpGroupsState(); + + return changed; +} + +QStringList DomainServerSettingsManager::getAllNames() const { + QStringList result; + foreach (auto key, _agentPermissions.keys()) { + result << key.first.toLower(); + } + return result; +} + +NodePermissions DomainServerSettingsManager::getStandardPermissionsForName(const NodePermissionsKey& name) const { if (_standardAgentPermissions.contains(name)) { return *(_standardAgentPermissions[name].get()); } @@ -534,55 +605,60 @@ NodePermissions DomainServerSettingsManager::getStandardPermissionsForName(const } NodePermissions DomainServerSettingsManager::getPermissionsForName(const QString& name) const { - if (_agentPermissions.contains(name)) { - return *(_agentPermissions[name].get()); + NodePermissionsKey nameKey = NodePermissionsKey(name, 0); + if (_agentPermissions.contains(nameKey)) { + return *(_agentPermissions[nameKey].get()); } NodePermissions nullPermissions; nullPermissions.setAll(false); return nullPermissions; } -NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QString& groupname) const { - if (_groupPermissions.contains(groupname)) { - return *(_groupPermissions[groupname].get()); +NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QString& groupName, int rank) const { + NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rank); + if (_groupPermissions.contains(groupRankKey)) { + return *(_groupPermissions[groupRankKey].get()); } NodePermissions nullPermissions; nullPermissions.setAll(false); return nullPermissions; } -NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QUuid& groupID) const { - if (!_groupByID.contains(groupID)) { +NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QUuid& groupID, int rank) const { + GroupByUUIDKey byUUIDKey = GroupByUUIDKey(groupID, rank); + if (!_groupPermissionsByUUID.contains(byUUIDKey)) { NodePermissions nullPermissions; nullPermissions.setAll(false); return nullPermissions; } - QString groupName = _groupByID[groupID]->getID(); - return getPermissionsForGroup(groupName); + NodePermissionsKey groupKey = _groupPermissionsByUUID[byUUIDKey]->getKey(); + return getPermissionsForGroup(groupKey.first, groupKey.second); } - -NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QString& groupname) const { - if (_groupForbiddens.contains(groupname)) { - return *(_groupForbiddens[groupname].get()); +NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QString& groupName, int rank) const { + NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rank); + if (_groupForbiddens.contains(groupRankKey)) { + return *(_groupForbiddens[groupRankKey].get()); } NodePermissions nullForbiddens; + // XXX should this be setAll(true) ? nullForbiddens.setAll(false); return nullForbiddens; } -NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid& groupID) const { - if (!_groupByID.contains(groupID)) { +NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid& groupID, int rank) const { + GroupByUUIDKey byUUIDKey = GroupByUUIDKey(groupID, rank); + if (!_groupForbiddensByUUID.contains(byUUIDKey)) { NodePermissions nullForbiddens; + // XXX should this be setAll(true) ? nullForbiddens.setAll(false); return nullForbiddens; } - QString groupName = _groupByID[groupID]->getID(); - return getForbiddensForGroup(groupName); + + NodePermissionsKey groupKey = _groupForbiddensByUUID[byUUIDKey]->getKey(); + return getForbiddensForGroup(groupKey.first, groupKey.second); } - - QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath); @@ -854,7 +930,8 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV sortPermissions(); } -QJsonObject DomainServerSettingsManager::settingDescriptionFromGroup(const QJsonObject& groupObject, const QString& settingName) { +QJsonObject DomainServerSettingsManager::settingDescriptionFromGroup(const QJsonObject& groupObject, + const QString& settingName) { foreach(const QJsonValue& settingValue, groupObject[DESCRIPTION_SETTINGS_KEY].toArray()) { QJsonObject settingObject = settingValue.toObject(); if (settingObject[DESCRIPTION_NAME_KEY].toString() == settingName) { @@ -967,6 +1044,12 @@ bool permissionVariantLessThan(const QVariant &v1, const QVariant &v2) { !m2.contains("permissions_id")) { return v1.toString() < v2.toString(); } + + if (m1.contains("rank_name") && m2.contains("rank_name") && + m1["permissions_id"].toString() == m2["permissions_id"].toString()) { + return m1["rank_name"].toString() < m2["rank_name"].toString(); + } + return m1["permissions_id"].toString() < m2["permissions_id"].toString(); } @@ -1003,62 +1086,94 @@ void DomainServerSettingsManager::persistToFile() { } } -void DomainServerSettingsManager::requestMissingGroupIDs() { +QStringList DomainServerSettingsManager::getAllKnownGroupNames() { + // extract all the group names from the group-permissions and group-forbiddens settings + QSet result; + + QHashIterator i_permissions(_groupPermissions.get()); + while (i_permissions.hasNext()) { + i_permissions.next(); + NodePermissionsKey key = i_permissions.key(); + result += key.first; + } + + QHashIterator i_forbiddens(_groupForbiddens.get()); + while (i_forbiddens.hasNext()) { + i_forbiddens.next(); + NodePermissionsKey key = i_forbiddens.key(); + result += key.first; + } + + return result.toList(); +} + +bool DomainServerSettingsManager::setGroupID(const QString& groupName, const QUuid& groupID) { + bool changed = false; + _groupIDs[groupName.toLower()] = groupID; + _groupNames[groupID] = groupName; + + QHashIterator i_permissions(_groupPermissions.get()); + while (i_permissions.hasNext()) { + i_permissions.next(); + NodePermissionsPointer perms = i_permissions.value(); + if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) { + changed = true; + perms->setGroupID(groupID); + } + } + + QHashIterator i_forbiddens(_groupForbiddens.get()); + while (i_forbiddens.hasNext()) { + i_forbiddens.next(); + NodePermissionsPointer perms = i_forbiddens.value(); + if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) { + changed = true; + perms->setGroupID(groupID); + } + } + + return changed; +} + +void DomainServerSettingsManager::apiRefreshGroupInformation() { + const int STALE_DATA_AGE = 600; // seconds + if (!DependencyManager::get()->hasAuthEndpoint()) { // can't yet. return; } - QHashIterator i_permissions(_groupPermissions.get()); - while (i_permissions.hasNext()) { - i_permissions.next(); - NodePermissionsPointer perms = i_permissions.value(); - if (!perms->getGroupID().isNull()) { - // we already know this group's ID + QStringList groupNames = getAllKnownGroupNames(); + foreach (QString groupName, groupNames) { + if (_groupIDs.contains(groupName.toLower())) { + // we already know about this one continue; } - - // make a call to metaverse api to turn the group name into a group ID - getGroupID(perms->getID()); + apiGetGroupID(groupName); } - QHashIterator i_forbiddens(_groupForbiddens.get()); - while (i_forbiddens.hasNext()) { - i_forbiddens.next(); - NodePermissionsPointer perms = i_forbiddens.value(); - if (!perms->getGroupID().isNull()) { - // we already know this group's ID - continue; - } - // make a call to metaverse api to turn the group name into a group ID - getGroupID(perms->getID()); + quint64 now = usecTimestampNow(); + foreach (QUuid groupID, _groupNames.keys()) { + if (now - _groupRanksLastFetched[groupID] > STALE_DATA_AGE * USECS_PER_SECOND) { + apiGetGroupRanks(groupID); + } } } -NodePermissionsPointer DomainServerSettingsManager::lookupGroupByID(const QUuid& id) { - if (_groupByID.contains(id)) { - return _groupByID[id]; - } - return nullptr; -} - -void DomainServerSettingsManager::getGroupID(const QString& groupname) { +void DomainServerSettingsManager::apiGetGroupID(const QString& groupName) { JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; - callbackParams.jsonCallbackMethod = "getGroupIDJSONCallback"; + callbackParams.jsonCallbackMethod = "apiGetGroupIDJSONCallback"; callbackParams.errorCallbackReceiver = this; - callbackParams.errorCallbackMethod = "getGroupIDErrorCallback"; + callbackParams.errorCallbackMethod = "apiGetGroupIDErrorCallback"; - const QString GET_GROUP_ID_PATH = "api/v1/groups/name/%1"; - - qDebug() << "************* Requesting group ID for group named" << groupname; - - DependencyManager::get()->sendRequest(GET_GROUP_ID_PATH.arg(groupname), + const QString GET_GROUP_ID_PATH = "api/v1/groups/names/%1"; + DependencyManager::get()->sendRequest(GET_GROUP_ID_PATH.arg(groupName), AccountManagerAuth::Required, QNetworkAccessManager::GetOperation, callbackParams); } -void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestReply) { +void DomainServerSettingsManager::apiGetGroupIDJSONCallback(QNetworkReply& requestReply) { // { // "data":{ // "groups":[{ @@ -1090,7 +1205,6 @@ void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestR // "status":"success" // } QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); - if (jsonObject["status"].toString() == "success") { QJsonArray groups = jsonObject["data"].toObject()["groups"].toArray(); for (int i = 0; i < groups.size(); i++) { @@ -1098,25 +1212,10 @@ void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestR QString groupName = group["name"].toString(); QUuid groupID = QUuid(group["id"].toString()); - bool found = false; - if (_groupPermissions.contains(groupName)) { - qDebug() << "ID for group:" << groupName << "is" << groupID; - _groupPermissions[groupName]->setGroupID(groupID); - _groupByID[groupID] = _groupPermissions[groupName]; - found = true; - } - if (_groupForbiddens.contains(groupName)) { - qDebug() << "ID for group:" << groupName << "is" << groupID; - _groupForbiddens[groupName]->setGroupID(groupID); - _groupByID[groupID] = _groupForbiddens[groupName]; - found = true; - } - - if (found) { + bool changed = setGroupID(groupName, groupID); + if (changed) { packPermissions(); - getGroupRanks(groupID); - } else { - qDebug() << "DomainServerSettingsManager::getGroupIDJSONCallback got response for unknown group:" << groupName; + apiGetGroupRanks(groupID); } } } else { @@ -1124,27 +1223,26 @@ void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestR } } -void DomainServerSettingsManager::getGroupIDErrorCallback(QNetworkReply& requestReply) { +void DomainServerSettingsManager::apiGetGroupIDErrorCallback(QNetworkReply& requestReply) { qDebug() << "******************** getGroupID api call failed:" << requestReply.error(); } -void DomainServerSettingsManager::getGroupRanks(const QUuid& groupID) { +void DomainServerSettingsManager::apiGetGroupRanks(const QUuid& groupID) { + _groupRanksLastFetched[groupID] = usecTimestampNow(); + JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; - callbackParams.jsonCallbackMethod = "getGroupRanksJSONCallback"; + callbackParams.jsonCallbackMethod = "apiGetGroupRanksJSONCallback"; callbackParams.errorCallbackReceiver = this; - callbackParams.errorCallbackMethod = "getGroupRanksErrorCallback"; + callbackParams.errorCallbackMethod = "apiGetGroupRanksErrorCallback"; const QString GET_GROUP_RANKS_PATH = "api/v1/groups/%1/ranks"; - - qDebug() << "************* Requesting group ranks for group" << groupID; - DependencyManager::get()->sendRequest(GET_GROUP_RANKS_PATH.arg(groupID.toString().mid(1,36)), AccountManagerAuth::Required, QNetworkAccessManager::GetOperation, callbackParams); } -void DomainServerSettingsManager::getGroupRanksJSONCallback(QNetworkReply& requestReply) { +void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply& requestReply) { // { // "current_page":1, // "data":{ @@ -1195,6 +1293,7 @@ void DomainServerSettingsManager::getGroupRanksJSONCallback(QNetworkReply& reque // "total_pages":1 // } + bool changed = false; QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); if (jsonObject["status"].toString() == "success") { QJsonObject groups = jsonObject["data"].toObject()["groups"].toObject(); @@ -1205,47 +1304,97 @@ void DomainServerSettingsManager::getGroupRanksJSONCallback(QNetworkReply& reque QJsonObject rank = ranks.at(rankIndex).toObject(); QString rankName = rank["name"].toString(); int rankOrder = rank["order"].toInt(); - qDebug() << "**** " << groupID << "rank name =" << rankName << " order =" << rankOrder; QVector& ranksForGroup = _groupRanks[groupID]; if (ranksForGroup.size() < rankOrder + 1) { ranksForGroup.resize(rankOrder + 1); + changed = true; + } + if (ranksForGroup[rankOrder] != rankName) { + ranksForGroup[rankOrder] = rankName; + changed = true; } - ranksForGroup[rankOrder] = rankName; } } + + changed |= ensurePermissionsForGroupRanks(); + if (changed) { + packPermissions(); + } } else { qDebug() << "getGroupRanks api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); } } -void DomainServerSettingsManager::getGroupRanksErrorCallback(QNetworkReply& requestReply) { +void DomainServerSettingsManager::apiGetGroupRanksErrorCallback(QNetworkReply& requestReply) { qDebug() << "******************** getGroupRanks api call failed:" << requestReply.error(); } -void DomainServerSettingsManager::recordGroupMembership(const QString& name, const QUuid groupID, bool isMember) { - _groupMembership[name][groupID] = isMember; +void DomainServerSettingsManager::recordGroupMembership(const QString& name, const QUuid groupID, int rank) { + if (rank >= 0) { + _groupMembership[name][groupID] = rank; + } else { + _groupMembership[name].remove(groupID); + } } -bool DomainServerSettingsManager::isGroupMember(const QString& name, const QUuid& groupID) { - return _groupMembership[name][groupID]; +int DomainServerSettingsManager::isGroupMember(const QString& name, const QUuid& groupID) { + const QHash& groupsForName = _groupMembership[name]; + if (groupsForName.contains(groupID)) { + return groupsForName[groupID]; + } + return -1; } QList DomainServerSettingsManager::getGroupIDs() { - QList result; - foreach (QString groupName, _groupPermissions.keys()) { - if (_groupPermissions[groupName]->isGroup()) { - result << _groupPermissions[groupName]->getGroupID(); + QSet result; + foreach (NodePermissionsKey groupKey, _groupPermissions.keys()) { + if (_groupPermissions[groupKey]->isGroup()) { + result += _groupPermissions[groupKey]->getGroupID(); } } - return result; + return result.toList(); } QList DomainServerSettingsManager::getBlacklistGroupIDs() { - QList result; - foreach (QString groupName, _groupForbiddens.keys()) { - if (_groupForbiddens[groupName]->isGroup()) { - result << _groupForbiddens[groupName]->getGroupID(); + QSet result; + foreach (NodePermissionsKey groupKey, _groupForbiddens.keys()) { + if (_groupForbiddens[groupKey]->isGroup()) { + result += _groupForbiddens[groupKey]->getGroupID(); } } - return result; + return result.toList(); +} + +void DomainServerSettingsManager::debugDumpGroupsState() { + qDebug() << "--------- GROUPS ---------"; + + qDebug() << "_groupPermissions:"; + foreach (NodePermissionsKey groupKey, _groupPermissions.keys()) { + NodePermissionsPointer perms = _groupPermissions[groupKey]; + qDebug() << "| " << groupKey << perms; + } + + qDebug() << "_groupIDs:"; + foreach (QString groupName, _groupIDs.keys()) { + qDebug() << "| " << groupName << "==>" << _groupIDs[groupName]; + } + + qDebug() << "_groupNames:"; + foreach (QUuid groupID, _groupNames.keys()) { + qDebug() << "| " << groupID << "==>" << _groupNames[groupID]; + } + + qDebug() << "_groupRanks:"; + foreach (QUuid groupID, _groupRanks.keys()) { + QVector& ranksForGroup = _groupRanks[groupID]; + QString readableRanks; + foreach (QString rankName, ranksForGroup) { + if (readableRanks == "") { + readableRanks = rankName; + } else { + readableRanks += "," + rankName; + } + } + qDebug() << "| " << groupID << "==>" << readableRanks; + } } diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 706717eda8..52e4b02e26 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -30,6 +30,8 @@ const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions"; const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions"; const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens"; +using GroupByUUIDKey = QPair; + class DomainServerSettingsManager : public QObject { Q_OBJECT public: @@ -46,40 +48,46 @@ public: QVariantMap& getDescriptorsMap(); // these give access to anonymous/localhost/logged-in settings from the domain-server settings page - bool haveStandardPermissionsForName(const QString& name) const { return _standardAgentPermissions.contains(name); } - NodePermissions getStandardPermissionsForName(const QString& name) const; + bool haveStandardPermissionsForName(const QString& name) const { return _standardAgentPermissions.contains(name, 0); } + NodePermissions getStandardPermissionsForName(const NodePermissionsKey& name) const; // these give access to permissions for specific user-names from the domain-server settings page - bool havePermissionsForName(const QString& name) const { return _agentPermissions.contains(name); } + bool havePermissionsForName(const QString& name) const { return _agentPermissions.contains(name, 0); } NodePermissions getPermissionsForName(const QString& name) const; - QStringList getAllNames() { return _agentPermissions.keys(); } + NodePermissions getPermissionsForName(const NodePermissionsKey& key) const { return getPermissionsForName(key.first); } + QStringList getAllNames() const; // these give access to permissions for specific groups from the domain-server settings page - bool havePermissionsForGroup(const QString& groupname) const { return _groupPermissions.contains(groupname); } - NodePermissions getPermissionsForGroup(const QString& groupname) const; - NodePermissions getPermissionsForGroup(const QUuid& groupID) const; + bool havePermissionsForGroup(const QString& groupName, int rank) const { + return _groupPermissions.contains(groupName, rank); + } + NodePermissions getPermissionsForGroup(const QString& groupName, int rank) const; + NodePermissions getPermissionsForGroup(const QUuid& groupID, int rank) const; // these remove permissions from users in certain groups - bool haveForbiddensForGroup(const QString& groupname) const { return _groupForbiddens.contains(groupname); } - NodePermissions getForbiddensForGroup(const QString& groupname) const; - NodePermissions getForbiddensForGroup(const QUuid& groupID) const; + bool haveForbiddensForGroup(const QString& groupName, int rank) const { return _groupForbiddens.contains(groupName, rank); } + NodePermissions getForbiddensForGroup(const QString& groupName, int rank) const; + NodePermissions getForbiddensForGroup(const QUuid& groupID, int rank) const; + + QStringList getAllKnownGroupNames(); + bool setGroupID(const QString& groupName, const QUuid& groupID); QList getGroupIDs(); QList getBlacklistGroupIDs(); // these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api void clearGroupMemberships(const QString& name) { _groupMembership[name].clear(); } - void recordGroupMembership(const QString& name, const QUuid groupID, bool isMember); - bool isGroupMember(const QString& name, const QUuid& groupID); + void recordGroupMembership(const QString& name, const QUuid groupID, int rank); + int isGroupMember(const QString& name, const QUuid& groupID); // returns rank or -1 if not a member signals: void updateNodePermissions(); public slots: - void getGroupIDJSONCallback(QNetworkReply& requestReply); - void getGroupIDErrorCallback(QNetworkReply& requestReply); - void getGroupRanksJSONCallback(QNetworkReply& requestReply); - void getGroupRanksErrorCallback(QNetworkReply& requestReply); + void apiGetGroupIDJSONCallback(QNetworkReply& requestReply); + void apiGetGroupIDErrorCallback(QNetworkReply& requestReply); + void apiGetGroupRanksJSONCallback(QNetworkReply& requestReply); + void apiGetGroupRanksErrorCallback(QNetworkReply& requestReply); private slots: void processSettingsRequestPacket(QSharedPointer message); @@ -105,26 +113,36 @@ private: void validateDescriptorsMap(); // these cause calls to metaverse's group api - void requestMissingGroupIDs(); - void getGroupID(const QString& groupname); - NodePermissionsPointer lookupGroupByID(const QUuid& id); - void getGroupRanks(const QUuid& groupID); + void apiRefreshGroupInformation(); + void apiGetGroupID(const QString& groupName); + void apiGetGroupRanks(const QUuid& groupID); void packPermissionsForMap(QString mapName, NodePermissionsMap& agentPermissions, QString keyPath); void packPermissions(); void unpackPermissions(); + bool ensurePermissionsForGroupRanks(); - NodePermissionsMap _standardAgentPermissions; // anonymous, logged-in, localhost + NodePermissionsMap _standardAgentPermissions; // anonymous, logged-in, localhost, friend-of-domain-owner NodePermissionsMap _agentPermissions; // specific account-names + NodePermissionsMap _groupPermissions; // permissions granted by membership to specific groups NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group - QHash _groupByID; // similar to _groupPermissions but key is group-id rather than name + // these are like _groupPermissions and _groupForbiddens but with uuids rather than group-names in the keys + QHash _groupPermissionsByUUID; + QHash _groupForbiddensByUUID; + + QHash _groupIDs; // keep track of group-name to group-id mappings + QHash _groupNames; // keep track of group-id to group-name mappings // remember the responses to api/v1/groups/%1/ranks - QHash> _groupRanks; + QHash> _groupRanks; // QHash> + QHash _groupRanksLastFetched; // when did we last update _groupRanks // keep track of answers to api queries about which users are in which groups - QHash> _groupMembership; + QHash> _groupMembership; // QHash> + + + void debugDumpGroupsState(); }; #endif // hifi_DomainServerSettingsManager_h diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index 023e3cb04a..e16b746b96 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -13,22 +13,27 @@ #include #include "NodePermissions.h" -QString NodePermissions::standardNameLocalhost = QString("localhost"); -QString NodePermissions::standardNameLoggedIn = QString("logged-in"); -QString NodePermissions::standardNameAnonymous = QString("anonymous"); -QString NodePermissions::standardNameFriends = QString("friends"); +NodePermissionsKey NodePermissions::standardNameLocalhost = NodePermissionsKey("localhost", 0); +NodePermissionsKey NodePermissions::standardNameLoggedIn = NodePermissionsKey("logged-in", 0); +NodePermissionsKey NodePermissions::standardNameAnonymous = NodePermissionsKey("anonymous", 0); +NodePermissionsKey NodePermissions::standardNameFriends = NodePermissionsKey("friends", 0); QStringList NodePermissions::standardNames = QList() - << NodePermissions::standardNameLocalhost - << NodePermissions::standardNameLoggedIn - << NodePermissions::standardNameAnonymous - << NodePermissions::standardNameFriends; + << NodePermissions::standardNameLocalhost.first + << NodePermissions::standardNameLoggedIn.first + << NodePermissions::standardNameAnonymous.first + << NodePermissions::standardNameFriends.first; NodePermissions::NodePermissions(QMap perms) { - _id = perms["permissions_id"].toString(); + _id = perms["permissions_id"].toString().toLower(); if (perms.contains("group_id")) { - _groupIDSet = true; _groupID = perms["group_id"].toUuid(); + if (!_groupID.isNull()) { + _groupIDSet = true; + } + } + if (perms.contains("rank")) { + _rank = perms["rank"].toInt(); } permissions = NodePermissions::Permissions(); @@ -41,11 +46,15 @@ NodePermissions::NodePermissions(QMap perms) { Permission::canConnectPastMaxCapacity : Permission::none; } -QVariant NodePermissions::toVariant() { +QVariant NodePermissions::toVariant(QVector rankNames) { QMap values; values["permissions_id"] = _id; if (_groupIDSet) { values["group_id"] = _groupID; + values["rank"] = _rank; + if (rankNames.size() > _rank) { + values["rank_name"] = rankNames[_rank]; + } } values["id_can_connect"] = can(Permission::canConnectToDomain); values["id_can_adjust_locks"] = can(Permission::canAdjustLocks); @@ -137,7 +146,9 @@ QDataStream& operator>>(QDataStream& in, NodePermissions& perms) { } QDebug operator<<(QDebug debug, const NodePermissions& perms) { - debug.nospace() << "[permissions: " << perms.getID() << " --"; + debug.nospace() << "[permissions: " << perms.getID() << "/" << perms.getUserName() << " -- "; + debug.nospace() << "rank=" << perms.getRank() + << ", groupID=" << perms.getGroupID() << "/" << (perms.isGroup() ? "y" : "n"); if (perms.can(NodePermissions::Permission::canConnectToDomain)) { debug << " connect"; } diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 1dea560543..411312f708 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -20,30 +20,35 @@ class NodePermissions; using NodePermissionsPointer = std::shared_ptr; +using NodePermissionsKey = QPair; +using NodePermissionsKeyList = QList>; class NodePermissions { public: - NodePermissions() { _id = QUuid::createUuid().toString(); } - NodePermissions(const QString& name) { _id = name.toLower(); } + NodePermissions() { _id = QUuid::createUuid().toString(); _rank = 0; } + NodePermissions(const QString& name) { _id = name.toLower(); _rank = 0; } + NodePermissions(const NodePermissionsKey& key) { _id = key.first.toLower(); _rank = key.second; } NodePermissions(QMap perms); - QString getID() const { return _id; } + QString getID() const { return _id; } // a user-name or a group-name, not verified + int getRank() const { return _rank; } + NodePermissionsKey getKey() const { return NodePermissionsKey(_id, _rank); } - // the _id member isn't authenticated and _username is. + // the _id member isn't authenticated/verified and _username is. void setUserName(QString userName) { _userName = userName.toLower(); } - QString getUserName() { return _userName; } + QString getUserName() const { return _userName; } - void setGroupID(QUuid groupID) { _groupID = groupID; if (!groupID.isNull()) { _groupIDSet = true; } } - QUuid getGroupID() { return _groupID; } - bool isGroup() { return _groupIDSet; } + void setGroupID(QUuid groupID) { _groupID = groupID; if (!groupID.isNull()) { _groupIDSet = true; }} + QUuid getGroupID() const { return _groupID; } + bool isGroup() const { return _groupIDSet; } bool isAssignment { false }; // these 3 names have special meaning. - static QString standardNameLocalhost; - static QString standardNameLoggedIn; - static QString standardNameAnonymous; - static QString standardNameFriends; + static NodePermissionsKey standardNameLocalhost; + static NodePermissionsKey standardNameLoggedIn; + static NodePermissionsKey standardNameAnonymous; + static NodePermissionsKey standardNameFriends; static QStringList standardNames; enum class Permission { @@ -58,7 +63,7 @@ public: Q_DECLARE_FLAGS(Permissions, Permission) Permissions permissions; - QVariant toVariant(); + QVariant toVariant(QVector rankNames = QVector()); void setAll(bool value); @@ -76,6 +81,7 @@ public: protected: QString _id; + int _rank { 0 }; // 0 unless this is for a group QString _userName; bool _groupIDSet { false }; @@ -88,15 +94,28 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(NodePermissions::Permissions) class NodePermissionsMap { public: NodePermissionsMap() { } - NodePermissionsPointer& operator[](const QString& key) { return _data[key.toLower()]; } - NodePermissionsPointer operator[](const QString& key) const { return _data.value(key.toLower()); } - bool contains(const QString& key) const { return _data.contains(key.toLower()); } - QList keys() const { return _data.keys(); } - QHash get() { return _data; } + NodePermissionsPointer& operator[](const NodePermissionsKey& key) { + NodePermissionsKey dataKey(key.first.toLower(), key.second); + if (!_data.contains(dataKey)) { + _data[dataKey] = NodePermissionsPointer(new NodePermissions(key)); + } + return _data[dataKey]; + } + NodePermissionsPointer operator[](const NodePermissionsKey& key) const { + return _data.value(NodePermissionsKey(key.first.toLower(), key.second)); + } + bool contains(const NodePermissionsKey& key) const { + return _data.contains(NodePermissionsKey(key.first.toLower(), key.second)); + } + bool contains(const QString& keyFirst, int keySecond) const { + return _data.contains(NodePermissionsKey(keyFirst.toLower(), keySecond)); + } + QList keys() const { return _data.keys(); } + QHash get() { return _data; } void clear() { _data.clear(); } private: - QHash _data; + QHash _data; }; From 55d76abf48ce783023e881a198362aee09b5495b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 6 Jul 2016 16:42:31 -0700 Subject: [PATCH 24/98] back-out accidently changed api urls --- domain-server/resources/web/settings/js/settings.js | 3 +-- domain-server/src/DomainServer.cpp | 3 +-- libraries/networking/src/NetworkingConstants.h | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index ed40b4c520..25cf860f54 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1,7 +1,6 @@ var Settings = { showAdvanced: false, - // METAVERSE_URL: 'https://metaverse.highfidelity.com', - METAVERSE_URL: 'http://localhost:3000', + METAVERSE_URL: 'https://metaverse.highfidelity.com', ADVANCED_CLASS: 'advanced-setting', TRIGGER_CHANGE_CLASS: 'trigger-change', DATA_ROW_CLASS: 'value-row', diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d5bf8d9adc..0126afe059 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -44,8 +44,7 @@ int const DomainServer::EXIT_CODE_REBOOT = 234923; -// const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com"; -const QString ICE_SERVER_DEFAULT_HOSTNAME = "localhost"; +const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com"; DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 981d5a7b3a..a512ae8887 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -15,8 +15,7 @@ #include namespace NetworkingConstants { - // const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com"); - const QUrl METAVERSE_SERVER_URL = QUrl("http://localhost:3000"); + const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com"); } #endif // hifi_NetworkingConstants_h From 0e86be86600a899b1095e4614df2eda94da171ed Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 13 Jul 2016 14:48:23 -0700 Subject: [PATCH 25/98] use dev metaverse --- domain-server/resources/describe-settings.json | 8 -------- domain-server/resources/web/settings/js/settings.js | 4 +++- domain-server/src/DomainServer.cpp | 4 +++- libraries/networking/src/NetworkingConstants.h | 4 +++- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 885a289db4..a81b58a30e 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -618,14 +618,6 @@ "name": "permissions_id", "label": "Group Name" }, - { - "name": "rank", - "label": "Rank" - }, - { - "name": "rank_name", - "label": "Rank Name" - }, { "name": "group_id", "label": "Group ID", diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 25cf860f54..54be33e764 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1,6 +1,8 @@ var Settings = { showAdvanced: false, - METAVERSE_URL: 'https://metaverse.highfidelity.com', + // METAVERSE_URL: 'https://metaverse.highfidelity.com', + // METAVERSE_URL: 'http://localhost:3000', + METAVERSE_URL: 'https://devdataweb.highfidelity.com', ADVANCED_CLASS: 'advanced-setting', TRIGGER_CHANGE_CLASS: 'trigger-change', DATA_ROW_CLASS: 'value-row', diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 0395f3a766..b7c61b600a 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -44,7 +44,9 @@ int const DomainServer::EXIT_CODE_REBOOT = 234923; -const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com"; +// const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com"; +// const QString ICE_SERVER_DEFAULT_HOSTNAME = "localhost"; +const QString ICE_SERVER_DEFAULT_HOSTNAME = "devdataweb.highfidelity.com"; DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index a512ae8887..62a8f29088 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -15,7 +15,9 @@ #include namespace NetworkingConstants { - const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com"); + // const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com"); + // const QUrl METAVERSE_SERVER_URL = QUrl("http://localhost:3000"); + const QUrl METAVERSE_SERVER_URL = QUrl("https://devdataweb.highfidelity.com"); } #endif // hifi_NetworkingConstants_h From 641746f2c0b45e08e472b2a6e114929866499182 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 13 Jul 2016 15:29:00 -0700 Subject: [PATCH 26/98] fix describe-settings --- domain-server/resources/describe-settings.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 6f17b7d76a..dde318b281 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -618,6 +618,14 @@ "name": "permissions_id", "label": "Group Name" }, + { + "name": "rank", + "label": "Rank" + }, + { + "name": "rank_name", + "label": "Rank Name" + }, { "name": "group_id", "label": "Group ID", From 599b8922964954190342a3be692804597f18419d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 14 Jul 2016 11:20:57 -0700 Subject: [PATCH 27/98] refresh cached group information every 15 seconds --- domain-server/src/DomainGatekeeper.cpp | 23 ++++++++++++++++++- domain-server/src/DomainGatekeeper.h | 2 ++ domain-server/src/DomainServer.cpp | 14 +++++++++++ domain-server/src/DomainServer.h | 15 +++++++----- .../src/DomainServerSettingsManager.cpp | 11 +-------- .../src/DomainServerSettingsManager.h | 5 +++- 6 files changed, 52 insertions(+), 18 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index f85766105e..968d56ca84 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -296,7 +296,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect QString verifiedUsername; if (!username.isEmpty() && verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { - // they are sent us a username and the signature verifies it + // they sent us a username and the signature verifies it userPerms.setUserName(username); verifiedUsername = username; getGroupMemberships(username); @@ -699,6 +699,7 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) { // loop through the groups mentioned on the settings page and ask if this user is in each. The replies // will be received asynchronously and permissions will be updated as the answers come in. QList groupIDs = _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs(); + // TODO -- use alternative that allows checking entire group list in one call foreach (QUuid groupID, groupIDs) { if (groupID.isNull()) { continue; @@ -789,3 +790,23 @@ void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requ void DomainGatekeeper::getDomainOwnerFriendsListErrorCallback(QNetworkReply& requestReply) { qDebug() << "getDomainOwnerFriendsList api call failed:" << requestReply.error(); } + +void DomainGatekeeper::refreshGroupsCache() { + // if agents are connected to this domain, refresh our cached information about groups and memberships in such. + + getDomainOwnerFriendsList(); + + int agentCount = 0; + auto nodeList = DependencyManager::get(); + nodeList->eachNode([&](const SharedNodePointer& node) { + if (!node->getPermissions().isAssignment) { + // this node is an agent + getGroupMemberships(node->getPermissions().getUserName()); + agentCount++; + } + }); + + if (agentCount > 0) { + _server->_settingsManager.apiRefreshGroupInformation(); + } +} diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 7d9cfe91e3..a5ff5d90a7 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -59,6 +59,8 @@ public slots: void getDomainOwnerFriendsListJSONCallback(QNetworkReply& requestReply); void getDomainOwnerFriendsListErrorCallback(QNetworkReply& requestReply); + void refreshGroupsCache(); + signals: void killNode(SharedNodePointer node); void connectedNode(SharedNodePointer node); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b7c61b600a..e1ee4906a2 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "DomainServerNodeData.h" #include "NodeConnectionData.h" @@ -108,6 +109,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : connect(&_settingsManager, &DomainServerSettingsManager::updateNodePermissions, &_gatekeeper, &DomainGatekeeper::updateNodePermissions); + setupGroupCacheRefresh(); + // if we were given a certificate/private key or oauth credentials they must succeed if (!(optionallyReadX509KeyAndCertificate() && optionallySetupOAuth())) { return; @@ -2328,3 +2331,14 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) { // immediately send an update to the metaverse API when our ice-server changes sendICEServerAddressToMetaverseAPI(); } + +void DomainServer::setupGroupCacheRefresh() { + const int REFRESH_GROUPS_INTERVAL_MSECS = 15 * MSECS_PER_SECOND; + + if (!_metaverseGroupCacheTimer) { + // setup a timer to refresh this server's cached group details + _metaverseGroupCacheTimer = new QTimer { this }; + connect(_metaverseGroupCacheTimer, &QTimer::timeout, &_gatekeeper, &DomainGatekeeper::refreshGroupsCache); + _metaverseGroupCacheTimer->start(REFRESH_GROUPS_INTERVAL_MSECS); + } +} diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 138cb9ca2d..4004333789 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -41,7 +41,7 @@ class DomainServer : public QCoreApplication, public HTTPSRequestHandler { public: DomainServer(int argc, char* argv[]); ~DomainServer(); - + static int const EXIT_CODE_REBOOT; bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); @@ -64,7 +64,7 @@ public slots: void processNodeDisconnectRequestPacket(QSharedPointer message); void processICEServerHeartbeatDenialPacket(QSharedPointer message); void processICEServerHeartbeatACK(QSharedPointer message); - + private slots: void aboutToQuit(); @@ -74,7 +74,7 @@ private slots: void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr); void sendHeartbeatToMetaverse() { sendHeartbeatToMetaverse(QString()); } void sendHeartbeatToIceServer(); - + void handleConnectedNode(SharedNodePointer newNode); void handleTempDomainSuccess(QNetworkReply& requestReply); @@ -96,7 +96,7 @@ signals: void iceServerChanged(); void userConnected(); void userDisconnected(); - + private: const QUuid& getID(); @@ -136,7 +136,7 @@ private: SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment); void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment); void addStaticAssignmentsToQueue(); - + QUrl oauthRedirectURL(); QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid()); @@ -151,7 +151,9 @@ private: QJsonObject jsonForSocket(const HifiSockAddr& socket); QJsonObject jsonObjectForNode(const SharedNodePointer& node); - + + void setupGroupCacheRefresh(); + DomainGatekeeper _gatekeeper; HTTPManager _httpManager; @@ -184,6 +186,7 @@ private: DomainMetadata* _metadata { nullptr }; QTimer* _iceHeartbeatTimer { nullptr }; QTimer* _metaverseHeartbeatTimer { nullptr }; + QTimer* _metaverseGroupCacheTimer { nullptr }; QList _iceServerAddresses; QSet _failedIceServerAddresses; diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 3f44b9a293..b9715240ca 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -515,10 +515,6 @@ void DomainServerSettingsManager::unpackPermissions() { packPermissions(); } - // attempt to retrieve any missing group-IDs, etc - apiRefreshGroupInformation(); - - #ifdef WANT_DEBUG qDebug() << "--------------- permissions ---------------------"; QList> permissionsSets; @@ -1136,8 +1132,6 @@ bool DomainServerSettingsManager::setGroupID(const QString& groupName, const QUu } void DomainServerSettingsManager::apiRefreshGroupInformation() { - const int STALE_DATA_AGE = 600; // seconds - if (!DependencyManager::get()->hasAuthEndpoint()) { // can't yet. return; @@ -1152,11 +1146,8 @@ void DomainServerSettingsManager::apiRefreshGroupInformation() { apiGetGroupID(groupName); } - quint64 now = usecTimestampNow(); foreach (QUuid groupID, _groupNames.keys()) { - if (now - _groupRanksLastFetched[groupID] > STALE_DATA_AGE * USECS_PER_SECOND) { - apiGetGroupRanks(groupID); - } + apiGetGroupRanks(groupID); } } diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 52e4b02e26..f59d76bd1e 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -80,6 +80,10 @@ public: void recordGroupMembership(const QString& name, const QUuid groupID, int rank); int isGroupMember(const QString& name, const QUuid& groupID); // returns rank or -1 if not a member + // calls http api to refresh group information + void apiRefreshGroupInformation(); + + signals: void updateNodePermissions(); @@ -113,7 +117,6 @@ private: void validateDescriptorsMap(); // these cause calls to metaverse's group api - void apiRefreshGroupInformation(); void apiGetGroupID(const QString& groupName); void apiGetGroupRanks(const QUuid& groupID); From 8e6b3ed8c22510619d0da7e8fc60fb50b92d719e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 14 Jul 2016 12:24:44 -0700 Subject: [PATCH 28/98] rename variable to make its use more clear --- domain-server/src/DomainGatekeeper.cpp | 11 +++++++---- libraries/networking/src/NodePermissions.cpp | 2 +- libraries/networking/src/NodePermissions.h | 6 +++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 968d56ca84..a5ab4ce410 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -133,7 +133,7 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous); qDebug() << "user-permissions: unverified or no username, so:" << userPerms; } else { - userPerms.setUserName(verifiedUsername); + userPerms.setVerifiedUserName(verifiedUsername); if (_server->_settingsManager.havePermissionsForName(verifiedUsername)) { userPerms = _server->_settingsManager.getPermissionsForName(verifiedUsername); qDebug() << "user-permissions: specific user matches, so:" << userPerms; @@ -185,7 +185,7 @@ void DomainGatekeeper::updateNodePermissions() { limitedNodeList->eachNode([this, limitedNodeList, &nodesToKill](const SharedNodePointer& node){ // the id and the username in NodePermissions will often be the same, but id is set before // authentication and username is only set once they user's key has been confirmed. - QString username = node->getPermissions().getUserName(); + QString username = node->getPermissions().getVerifiedUserName(); NodePermissions userPerms(NodePermissionsKey(username, 0)); if (node->getPermissions().isAssignment) { @@ -297,7 +297,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect QString verifiedUsername; if (!username.isEmpty() && verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { // they sent us a username and the signature verifies it - userPerms.setUserName(username); + userPerms.setVerifiedUserName(username); verifiedUsername = username; getGroupMemberships(username); getDomainOwnerFriendsList(); @@ -801,7 +801,10 @@ void DomainGatekeeper::refreshGroupsCache() { nodeList->eachNode([&](const SharedNodePointer& node) { if (!node->getPermissions().isAssignment) { // this node is an agent - getGroupMemberships(node->getPermissions().getUserName()); + QString verifiedUserName = node->getPermissions().getVerifiedUserName(); + if (verifiedUserName != "") { + getGroupMemberships(verifiedUserName); + } agentCount++; } }); diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index e16b746b96..29b05fc837 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -146,7 +146,7 @@ QDataStream& operator>>(QDataStream& in, NodePermissions& perms) { } QDebug operator<<(QDebug debug, const NodePermissions& perms) { - debug.nospace() << "[permissions: " << perms.getID() << "/" << perms.getUserName() << " -- "; + debug.nospace() << "[permissions: " << perms.getID() << "/" << perms.getVerifiedUserName() << " -- "; debug.nospace() << "rank=" << perms.getRank() << ", groupID=" << perms.getGroupID() << "/" << (perms.isGroup() ? "y" : "n"); if (perms.can(NodePermissions::Permission::canConnectToDomain)) { diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 411312f708..541f90aee8 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -35,8 +35,8 @@ public: NodePermissionsKey getKey() const { return NodePermissionsKey(_id, _rank); } // the _id member isn't authenticated/verified and _username is. - void setUserName(QString userName) { _userName = userName.toLower(); } - QString getUserName() const { return _userName; } + void setVerifiedUserName(QString userName) { _verifiedUserName = userName.toLower(); } + QString getVerifiedUserName() const { return _verifiedUserName; } void setGroupID(QUuid groupID) { _groupID = groupID; if (!groupID.isNull()) { _groupIDSet = true; }} QUuid getGroupID() const { return _groupID; } @@ -82,7 +82,7 @@ public: protected: QString _id; int _rank { 0 }; // 0 unless this is for a group - QString _userName; + QString _verifiedUserName; bool _groupIDSet { false }; QUuid _groupID; From 97b40ab87d904abac8e969df4d9cee02d829c2d1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 21 Jul 2016 14:04:55 -0700 Subject: [PATCH 29/98] adapting to api changes --- domain-server/src/DomainGatekeeper.cpp | 23 +++++++- .../src/DomainServerSettingsManager.cpp | 59 ++++++++----------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index a5ab4ce410..32c0eb00c9 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -153,18 +153,24 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, int rank = _server->_settingsManager.isGroupMember(verifiedUsername, groupID); if (rank >= 0) { userPerms |= _server->_settingsManager.getPermissionsForGroup(groupID, rank); - qDebug() << "user-permissions: user is in group:" << groupID << "so:" << userPerms; + qDebug() << "user-permissions: user is in group:" << groupID << " rank:" << rank << "so:" << userPerms; } } // if this user is a known member of a blacklist group, remove the implied permissions + qDebug() << "------------------ checking blacklists ----------------------"; + qDebug() << _server->_settingsManager.getBlacklistGroupIDs(); foreach (QUuid groupID, _server->_settingsManager.getBlacklistGroupIDs()) { if (_server->_settingsManager.isGroupMember(verifiedUsername, groupID)) { int rank = _server->_settingsManager.isGroupMember(verifiedUsername, groupID); + qDebug() << groupID << verifiedUsername << "is member with rank" << rank; if (rank >= 0) { userPerms &= ~_server->_settingsManager.getForbiddensForGroup(groupID, rank); - qDebug() << "user-permissions: user is in blacklist group:" << groupID << "so:" << userPerms; + qDebug() << "user-permissions: user is in blacklist group:" << groupID << " rank:" << rank + << "so:" << userPerms; } + } else { + qDebug() << groupID << verifiedUsername << "is not member."; } } } @@ -739,14 +745,23 @@ void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) // "status":"success" // } + + + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + + qDebug() << "********* getIsGroupMember api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); + + if (jsonObject["status"].toString() == "success") { QJsonObject data = jsonObject["data"].toObject(); QJsonObject groups = data["groups"].toObject(); QString username = data["username"].toString(); _server->_settingsManager.clearGroupMemberships(username); foreach (auto groupID, groups.keys()) { - _server->_settingsManager.recordGroupMembership(username, groupID, true); + QJsonObject group = groups[groupID].toObject(); + int order = group["order"].toInt(); + _server->_settingsManager.recordGroupMembership(username, groupID, order); } } else { qDebug() << "getIsGroupMember api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); @@ -812,4 +827,6 @@ void DomainGatekeeper::refreshGroupsCache() { if (agentCount > 0) { _server->_settingsManager.apiRefreshGroupInformation(); } + + updateNodePermissions(); } diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index b9715240ca..53b90551c1 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -481,7 +481,7 @@ void DomainServerSettingsManager::unpackPermissions() { } if (perms->isGroup()) { // the group-id was cached. hook-up the uuid in the uuid->group hash - _groupForbiddensByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRank())] = _groupPermissions[idKey]; + _groupForbiddensByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRank())] = _groupForbiddens[idKey]; needPack |= setGroupID(perms->getID(), perms->getGroupID()); } } @@ -714,8 +714,6 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent()); QJsonObject postedObject = postedDocument.object(); - qDebug() << "DomainServerSettingsManager postedObject -" << postedObject; - // we recurse one level deep below each group for the appropriate setting bool restartRequired = recurseJSONObjectAndOverwriteSettings(postedObject); @@ -747,6 +745,9 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection rootObject[SETTINGS_RESPONSE_VALUE_KEY] = responseObjectForType("", true); rootObject[SETTINGS_RESPONSE_LOCKED_VALUES_KEY] = QJsonDocument::fromVariant(_configMap.getMasterConfig()).object(); + + qDebug() << QJsonDocument(rootObject).toJson(QJsonDocument::Indented); + connection->respond(HTTPConnection::StatusCode200, QJsonDocument(rootObject).toJson(), "application/json"); } @@ -1149,6 +1150,8 @@ void DomainServerSettingsManager::apiRefreshGroupInformation() { foreach (QUuid groupID, _groupNames.keys()) { apiGetGroupRanks(groupID); } + + unpackPermissions(); } void DomainServerSettingsManager::apiGetGroupID(const QString& groupName) { @@ -1234,13 +1237,17 @@ void DomainServerSettingsManager::apiGetGroupRanks(const QUuid& groupID) { } void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply& requestReply) { + + // { - // "current_page":1, // "data":{ // "groups":{ - // "fd55479a-265d-4990-854e-3d04214ad1b0":{ + // "d3500f49-0655-4b1b-9846-ff8dd1b03351":{ + // "members_count":1, // "ranks":[ // { + // "id":"7979b774-e7f8-436c-9df1-912f1019f32f", + // "members_count":1, // "name":"owner", // "order":0, // "permissions":{ @@ -1248,44 +1255,24 @@ void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply& re // "custom_2":false, // "custom_3":false, // "custom_4":false, - // "del_group":true, - // "invite_member":true, - // "kick_member":true, + // "edit_group":true, + // "edit_member":true, + // "edit_rank":true, // "list_members":true, - // "mv_group":true, - // "query_members":true, - // "rank_member":true - // } - // }, - // { - // "name":"admin", - // "order":1, - // "permissions":{ - // "custom_1":false, - // "custom_2":false, - // "custom_3":false, - // "custom_4":false, - // "del_group":false, - // "invite_member":false, - // "kick_member":false, - // "list_members":false, - // "mv_group":false, - // "query_members":false, - // "rank_member":false + // "list_permissions":true, + // "list_ranks":true, + // "query_member":true // } // } // ] // } // } - // }, - // "per_page":30, - // "status":"success", - // "total_entries":2, - // "total_pages":1 + // },"status":"success" // } bool changed = false; QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + if (jsonObject["status"].toString() == "success") { QJsonObject groups = jsonObject["data"].toObject()["groups"].toObject(); foreach (auto groupID, groups.keys()) { @@ -1365,6 +1352,12 @@ void DomainServerSettingsManager::debugDumpGroupsState() { qDebug() << "| " << groupKey << perms; } + qDebug() << "_groupForbiddens:"; + foreach (NodePermissionsKey groupKey, _groupForbiddens.keys()) { + NodePermissionsPointer perms = _groupForbiddens[groupKey]; + qDebug() << "| " << groupKey << perms; + } + qDebug() << "_groupIDs:"; foreach (QString groupName, _groupIDs.keys()) { qDebug() << "| " << groupName << "==>" << _groupIDs[groupName]; From adba4cde0b166274165c0896a8b01715260a5ff5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 22 Jul 2016 11:40:23 -0700 Subject: [PATCH 30/98] book-keeping around groups and ranks --- .../resources/describe-settings.json | 20 ++- domain-server/src/DomainGatekeeper.cpp | 93 +++++++---- domain-server/src/DomainGatekeeper.h | 2 +- .../src/DomainServerSettingsManager.cpp | 153 +++++++++++++----- .../src/DomainServerSettingsManager.h | 30 ++-- libraries/networking/src/GroupRank.h | 36 +++++ libraries/networking/src/NodePermissions.cpp | 15 +- libraries/networking/src/NodePermissions.h | 23 +-- 8 files changed, 263 insertions(+), 109 deletions(-) create mode 100644 libraries/networking/src/GroupRank.h diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index bf238e657f..aca288c452 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -526,7 +526,7 @@ "groups": [ { "label": "Group", - "span": 4 + "span": 5 }, { "label": "Permissions ?", @@ -540,8 +540,12 @@ "label": "Group Name" }, { - "name": "rank", - "label": "Rank" + "name": "rank_id", + "label": "Rank ID" + }, + { + "name": "rank_order", + "label": "Rank Order" }, { "name": "rank_name", @@ -605,7 +609,7 @@ "groups": [ { "label": "Group", - "span": 4 + "span": 5 }, { "label": "Permissions ?", @@ -619,8 +623,12 @@ "label": "Group Name" }, { - "name": "rank", - "label": "Rank" + "name": "rank_id", + "label": "Rank ID" + }, + { + "name": "rank_order", + "label": "Rank Order" }, { "name": "rank_name", diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 32c0eb00c9..08fe40faff 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -150,10 +150,12 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, // if this user is a known member of a group, give them the implied permissions foreach (QUuid groupID, _server->_settingsManager.getGroupIDs()) { - int rank = _server->_settingsManager.isGroupMember(verifiedUsername, groupID); - if (rank >= 0) { - userPerms |= _server->_settingsManager.getPermissionsForGroup(groupID, rank); - qDebug() << "user-permissions: user is in group:" << groupID << " rank:" << rank << "so:" << userPerms; + QUuid rankID = _server->_settingsManager.isGroupMember(verifiedUsername, groupID); + if (rankID != QUuid()) { + userPerms |= _server->_settingsManager.getPermissionsForGroup(groupID, rankID); + + GroupRank rank = _server->_settingsManager.getGroupRank(groupID, rankID); + qDebug() << "user-permissions: user is in group:" << groupID << " rank:" << rank.name << "so:" << userPerms; } } @@ -161,12 +163,14 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, qDebug() << "------------------ checking blacklists ----------------------"; qDebug() << _server->_settingsManager.getBlacklistGroupIDs(); foreach (QUuid groupID, _server->_settingsManager.getBlacklistGroupIDs()) { - if (_server->_settingsManager.isGroupMember(verifiedUsername, groupID)) { - int rank = _server->_settingsManager.isGroupMember(verifiedUsername, groupID); - qDebug() << groupID << verifiedUsername << "is member with rank" << rank; - if (rank >= 0) { - userPerms &= ~_server->_settingsManager.getForbiddensForGroup(groupID, rank); - qDebug() << "user-permissions: user is in blacklist group:" << groupID << " rank:" << rank + QUuid rankID = _server->_settingsManager.isGroupMember(verifiedUsername, groupID); + if (rankID != QUuid()) { + QUuid rankID = _server->_settingsManager.isGroupMember(verifiedUsername, groupID); + if (rankID != QUuid()) { + userPerms &= ~_server->_settingsManager.getForbiddensForGroup(groupID, rankID); + + GroupRank rank = _server->_settingsManager.getGroupRank(groupID, rankID); + qDebug() << "user-permissions: user is in blacklist group:" << groupID << " rank:" << rank.name << "so:" << userPerms; } } else { @@ -197,6 +201,7 @@ void DomainGatekeeper::updateNodePermissions() { if (node->getPermissions().isAssignment) { // this node is an assignment-client userPerms.isAssignment = true; + userPerms.permissions |= NodePermissions::Permission::canConnectToDomain; userPerms.permissions |= NodePermissions::Permission::canAdjustLocks; userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities; userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities; @@ -263,9 +268,10 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo // cleanup the PendingAssignedNodeData for this assignment now that it's connecting _pendingAssignedNodes.erase(it); - // always allow assignment clients to create and destroy entities NodePermissions userPerms; userPerms.isAssignment = true; + userPerms.permissions |= NodePermissions::Permission::canConnectToDomain; + // always allow assignment clients to create and destroy entities userPerms.permissions |= NodePermissions::Permission::canAdjustLocks; userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities; userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities; @@ -701,33 +707,63 @@ void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer } } +// void DomainGatekeeper::getGroupMemberships(const QString& username) { +// // loop through the groups mentioned on the settings page and ask if this user is in each. The replies +// // will be received asynchronously and permissions will be updated as the answers come in. +// QList groupIDs = _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs(); +// // TODO -- use alternative that allows checking entire group list in one call +// foreach (QUuid groupID, groupIDs) { +// if (groupID.isNull()) { +// continue; +// } +// getIsGroupMember(username, groupID); +// } +// } + +// void DomainGatekeeper::getIsGroupMember(const QString& username, const QUuid groupID) { +// JSONCallbackParameters callbackParams; +// callbackParams.jsonCallbackReceiver = this; +// callbackParams.jsonCallbackMethod = "getIsGroupMemberJSONCallback"; +// callbackParams.errorCallbackReceiver = this; +// callbackParams.errorCallbackMethod = "getIsGroupMemberErrorCallback"; + +// const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/%1/members/%2"; +// QString groupIDStr = groupID.toString().mid(1,36); +// DependencyManager::get()->sendRequest(GET_IS_GROUP_MEMBER_PATH.arg(groupIDStr).arg(username), +// AccountManagerAuth::Required, +// QNetworkAccessManager::GetOperation, callbackParams); +// } + + + void DomainGatekeeper::getGroupMemberships(const QString& username) { // loop through the groups mentioned on the settings page and ask if this user is in each. The replies // will be received asynchronously and permissions will be updated as the answers come in. - QList groupIDs = _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs(); - // TODO -- use alternative that allows checking entire group list in one call - foreach (QUuid groupID, groupIDs) { - if (groupID.isNull()) { - continue; - } - getIsGroupMember(username, groupID); - } -} -void DomainGatekeeper::getIsGroupMember(const QString& username, const QUuid groupID) { + QJsonObject json; + QSet groupIDSet; + foreach (QUuid groupID, _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs()) { + groupIDSet += groupID.toString().mid(1,36); + } + QJsonArray groupIDs = QJsonArray::fromStringList(groupIDSet.toList()); + json["groups"] = groupIDs; + JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; callbackParams.jsonCallbackMethod = "getIsGroupMemberJSONCallback"; callbackParams.errorCallbackReceiver = this; callbackParams.errorCallbackMethod = "getIsGroupMemberErrorCallback"; - const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/%1/members/%2"; - QString groupIDStr = groupID.toString().mid(1,36); - DependencyManager::get()->sendRequest(GET_IS_GROUP_MEMBER_PATH.arg(groupIDStr).arg(username), + const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/members/%2"; + DependencyManager::get()->sendRequest(GET_IS_GROUP_MEMBER_PATH.arg(username), AccountManagerAuth::Required, - QNetworkAccessManager::GetOperation, callbackParams); + QNetworkAccessManager::PostOperation, callbackParams, + QJsonDocument(json).toJson()); + } + + void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) { // { // "data":{ @@ -746,8 +782,6 @@ void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) // } - - QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); qDebug() << "********* getIsGroupMember api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); @@ -760,8 +794,9 @@ void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) _server->_settingsManager.clearGroupMemberships(username); foreach (auto groupID, groups.keys()) { QJsonObject group = groups[groupID].toObject(); - int order = group["order"].toInt(); - _server->_settingsManager.recordGroupMembership(username, groupID, order); + QJsonObject rank = group["rank"].toObject(); + QUuid rankID = QUuid(rank["id"].toString()); + _server->_settingsManager.recordGroupMembership(username, groupID, rankID); } } else { qDebug() << "getIsGroupMember api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index a5ff5d90a7..a18554338a 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -107,7 +107,7 @@ private: NodePermissions applyPermissionsForUser(bool isLocalUser, NodePermissions userPerms, QString verifiedUsername); void getGroupMemberships(const QString& username); - void getIsGroupMember(const QString& username, const QUuid groupID); + // void getIsGroupMember(const QString& username, const QUuid groupID); void getDomainOwnerFriendsList(); }; diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 53b90551c1..bd429029ed 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -347,11 +347,31 @@ void DomainServerSettingsManager::packPermissionsForMap(QString mapName, // convert details for each member of the subsection QVariantList* permissionsList = reinterpret_cast(permissions); (*permissionsList).clear(); - foreach (NodePermissionsKey userKey, agentPermissions.keys()) { + QList permissionsKeys = agentPermissions.keys(); + + // when a group is added from the domain-server settings page, the config map has a group-name with + // no ID or rank. We need to leave that there until we get a valid response back from the api. + // once we have the ranks and IDs, we need to delete the original entry so that it doesn't show + // up in the settings-page with undefined's after it. + QHash groupNamesWithRanks; + // note which groups have rank/ID information + foreach (NodePermissionsKey userKey, permissionsKeys) { + NodePermissionsPointer perms = agentPermissions[userKey]; + if (perms->getRankID() != QUuid()) { + groupNamesWithRanks[userKey.first] = true; + } + } + + // convert each group-name / rank-id pair to a variant-map + foreach (NodePermissionsKey userKey, permissionsKeys) { NodePermissionsPointer perms = agentPermissions[userKey]; if (perms->isGroup()) { - QVector rankNames = _groupRanks[perms->getGroupID()]; - *permissionsList += perms->toVariant(rankNames); + if (perms->getRankID() == QUuid() && groupNamesWithRanks.contains(userKey.first)) { + // skip over the entry that was created when the user added the group. + continue; + } + QHash& groupRanks = _groupRanks[perms->getGroupID()]; + *permissionsList += perms->toVariant(groupRanks); } else { *permissionsList += perms->toVariant(); } @@ -462,7 +482,7 @@ void DomainServerSettingsManager::unpackPermissions() { } if (perms->isGroup()) { // the group-id was cached. hook-up the uuid in the uuid->group hash - _groupPermissionsByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRank())] = _groupPermissions[idKey]; + _groupPermissionsByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRankID())] = _groupPermissions[idKey]; needPack |= setGroupID(perms->getID(), perms->getGroupID()); } } @@ -481,7 +501,7 @@ void DomainServerSettingsManager::unpackPermissions() { } if (perms->isGroup()) { // the group-id was cached. hook-up the uuid in the uuid->group hash - _groupForbiddensByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRank())] = _groupForbiddens[idKey]; + _groupForbiddensByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRankID())] = _groupForbiddens[idKey]; needPack |= setGroupID(perms->getID(), perms->getGroupID()); } } @@ -541,10 +561,10 @@ bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() { QList permissionGroupIDs = getGroupIDs(); foreach (QUuid groupID, permissionGroupIDs) { QString groupName = _groupNames[groupID]; - int rankCountForGroup = _groupRanks[groupID].size(); - for (int rank = 0; rank < rankCountForGroup; rank++) { - NodePermissionsKey nameKey = NodePermissionsKey(groupName, rank); - GroupByUUIDKey idKey = GroupByUUIDKey(groupID, rank); + QHash& ranksForGroup = _groupRanks[groupID]; + foreach (QUuid rankID, ranksForGroup.keys()) { + NodePermissionsKey nameKey = NodePermissionsKey(groupName, rankID); + GroupByUUIDKey idKey = GroupByUUIDKey(groupID, rankID); NodePermissionsPointer perms; if (_groupPermissions.contains(nameKey)) { perms = _groupPermissions[nameKey]; @@ -561,10 +581,10 @@ bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() { QList forbiddenGroupIDs = getBlacklistGroupIDs(); foreach (QUuid groupID, forbiddenGroupIDs) { QString groupName = _groupNames[groupID]; - int rankCountForGroup = _groupRanks[groupID].size(); - for (int rank = 0; rank < rankCountForGroup; rank++) { - NodePermissionsKey nameKey = NodePermissionsKey(groupName, rank); - GroupByUUIDKey idKey = GroupByUUIDKey(groupID, rank); + QHash& ranksForGroup = _groupRanks[groupID]; + foreach (QUuid rankID, ranksForGroup.keys()) { + NodePermissionsKey nameKey = NodePermissionsKey(groupName, rankID); + GroupByUUIDKey idKey = GroupByUUIDKey(groupID, rankID); NodePermissionsPointer perms; if (_groupForbiddens.contains(nameKey)) { perms = _groupForbiddens[nameKey]; @@ -610,8 +630,8 @@ NodePermissions DomainServerSettingsManager::getPermissionsForName(const QString return nullPermissions; } -NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QString& groupName, int rank) const { - NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rank); +NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QString& groupName, QUuid rankID) const { + NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rankID); if (_groupPermissions.contains(groupRankKey)) { return *(_groupPermissions[groupRankKey].get()); } @@ -620,8 +640,8 @@ NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QStrin return nullPermissions; } -NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QUuid& groupID, int rank) const { - GroupByUUIDKey byUUIDKey = GroupByUUIDKey(groupID, rank); +NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QUuid& groupID, QUuid rankID) const { + GroupByUUIDKey byUUIDKey = GroupByUUIDKey(groupID, rankID); if (!_groupPermissionsByUUID.contains(byUUIDKey)) { NodePermissions nullPermissions; nullPermissions.setAll(false); @@ -631,8 +651,8 @@ NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QUuid& return getPermissionsForGroup(groupKey.first, groupKey.second); } -NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QString& groupName, int rank) const { - NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rank); +NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QString& groupName, QUuid rankID) const { + NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rankID); if (_groupForbiddens.contains(groupRankKey)) { return *(_groupForbiddens[groupRankKey].get()); } @@ -642,8 +662,8 @@ NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QString return nullForbiddens; } -NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid& groupID, int rank) const { - GroupByUUIDKey byUUIDKey = GroupByUUIDKey(groupID, rank); +NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid& groupID, QUuid rankID) const { + GroupByUUIDKey byUUIDKey = GroupByUUIDKey(groupID, rankID); if (!_groupForbiddensByUUID.contains(byUUIDKey)) { NodePermissions nullForbiddens; // XXX should this be setAll(true) ? @@ -1042,9 +1062,14 @@ bool permissionVariantLessThan(const QVariant &v1, const QVariant &v2) { return v1.toString() < v2.toString(); } - if (m1.contains("rank_name") && m2.contains("rank_name") && + // if (m1.contains("rank_name") && m2.contains("rank_name") && + // m1["permissions_id"].toString() == m2["permissions_id"].toString()) { + // return m1["rank_name"].toString() < m2["rank_name"].toString(); + // } + + if (m1.contains("rank_order") && m2.contains("rank_order") && m1["permissions_id"].toString() == m2["permissions_id"].toString()) { - return m1["rank_name"].toString() < m2["rank_name"].toString(); + return m1["rank_order"].toInt() < m2["rank_order"].toInt(); } return m1["permissions_id"].toString() < m2["permissions_id"].toString(); @@ -1062,6 +1087,16 @@ void DomainServerSettingsManager::sortPermissions() { QList* permissionsList = reinterpret_cast(permissions); std::sort((*permissionsList).begin(), (*permissionsList).end(), permissionVariantLessThan); } + QVariant* groupPermissions = valueForKeyPath(_configMap.getUserConfig(), GROUP_PERMISSIONS_KEYPATH); + if (groupPermissions && groupPermissions->canConvert(QMetaType::QVariantList)) { + QList* permissionsList = reinterpret_cast(groupPermissions); + std::sort((*permissionsList).begin(), (*permissionsList).end(), permissionVariantLessThan); + } + QVariant* forbiddenPermissions = valueForKeyPath(_configMap.getUserConfig(), GROUP_FORBIDDENS_KEYPATH); + if (forbiddenPermissions && forbiddenPermissions->canConvert(QMetaType::QVariantList)) { + QList* permissionsList = reinterpret_cast(forbiddenPermissions); + std::sort((*permissionsList).begin(), (*permissionsList).end(), permissionVariantLessThan); + } } void DomainServerSettingsManager::persistToFile() { @@ -1138,10 +1173,15 @@ void DomainServerSettingsManager::apiRefreshGroupInformation() { return; } + bool changed = false; + QStringList groupNames = getAllKnownGroupNames(); foreach (QString groupName, groupNames) { - if (_groupIDs.contains(groupName.toLower())) { - // we already know about this one + QString lowerGroupName = groupName.toLower(); + if (_groupIDs.contains(lowerGroupName)) { + // we already know about this one. recall setGroupID in case the group has been + // added to another section (the same group is found in both groups and blacklists). + changed = setGroupID(groupName, _groupIDs[lowerGroupName]); continue; } apiGetGroupID(groupName); @@ -1151,6 +1191,12 @@ void DomainServerSettingsManager::apiRefreshGroupInformation() { apiGetGroupRanks(groupID); } + changed |= ensurePermissionsForGroupRanks(); + + if (changed) { + packPermissions(); + } + unpackPermissions(); } @@ -1222,8 +1268,6 @@ void DomainServerSettingsManager::apiGetGroupIDErrorCallback(QNetworkReply& requ } void DomainServerSettingsManager::apiGetGroupRanks(const QUuid& groupID) { - _groupRanksLastFetched[groupID] = usecTimestampNow(); - JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; callbackParams.jsonCallbackMethod = "apiGetGroupRanksJSONCallback"; @@ -1278,18 +1322,32 @@ void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply& re foreach (auto groupID, groups.keys()) { QJsonObject group = groups[groupID].toObject(); QJsonArray ranks = group["ranks"].toArray(); + + QHash& ranksForGroup = _groupRanks[groupID]; + QHash idsFromThisUpdate; + for (int rankIndex = 0; rankIndex < ranks.size(); rankIndex++) { QJsonObject rank = ranks.at(rankIndex).toObject(); - QString rankName = rank["name"].toString(); + + QUuid rankID = QUuid(rank["id"].toString()); int rankOrder = rank["order"].toInt(); - QVector& ranksForGroup = _groupRanks[groupID]; - if (ranksForGroup.size() < rankOrder + 1) { - ranksForGroup.resize(rankOrder + 1); + QString rankName = rank["name"].toString(); + int rankMembersCount = rank["members_count"].toInt(); + + GroupRank groupRank(rankID, rankOrder, rankName, rankMembersCount); + + if (ranksForGroup[rankID] != groupRank) { + ranksForGroup[rankID] = groupRank; changed = true; } - if (ranksForGroup[rankOrder] != rankName) { - ranksForGroup[rankOrder] = rankName; - changed = true; + + idsFromThisUpdate[rankID] = true; + } + + // clean up any that went away + foreach (QUuid rankID, ranksForGroup.keys()) { + if (!idsFromThisUpdate.contains(rankID)) { + ranksForGroup.remove(rankID); } } } @@ -1307,20 +1365,20 @@ void DomainServerSettingsManager::apiGetGroupRanksErrorCallback(QNetworkReply& r qDebug() << "******************** getGroupRanks api call failed:" << requestReply.error(); } -void DomainServerSettingsManager::recordGroupMembership(const QString& name, const QUuid groupID, int rank) { - if (rank >= 0) { - _groupMembership[name][groupID] = rank; +void DomainServerSettingsManager::recordGroupMembership(const QString& name, const QUuid groupID, QUuid rankID) { + if (rankID != QUuid()) { + _groupMembership[name][groupID] = rankID; } else { _groupMembership[name].remove(groupID); } } -int DomainServerSettingsManager::isGroupMember(const QString& name, const QUuid& groupID) { - const QHash& groupsForName = _groupMembership[name]; +QUuid DomainServerSettingsManager::isGroupMember(const QString& name, const QUuid& groupID) { + const QHash& groupsForName = _groupMembership[name]; if (groupsForName.contains(groupID)) { return groupsForName[groupID]; } - return -1; + return QUuid(); } QList DomainServerSettingsManager::getGroupIDs() { @@ -1370,9 +1428,10 @@ void DomainServerSettingsManager::debugDumpGroupsState() { qDebug() << "_groupRanks:"; foreach (QUuid groupID, _groupRanks.keys()) { - QVector& ranksForGroup = _groupRanks[groupID]; + QHash& ranksForGroup = _groupRanks[groupID]; QString readableRanks; - foreach (QString rankName, ranksForGroup) { + foreach (QUuid rankID, ranksForGroup.keys()) { + QString rankName = ranksForGroup[rankID].name; if (readableRanks == "") { readableRanks = rankName; } else { @@ -1381,4 +1440,14 @@ void DomainServerSettingsManager::debugDumpGroupsState() { } qDebug() << "| " << groupID << "==>" << readableRanks; } + + qDebug() << "_groupMembership"; + foreach (QString userName, _groupMembership.keys()) { + QHash& groupsForUser = _groupMembership[userName]; + QString line = ""; + foreach (QUuid groupID, groupsForUser.keys()) { + line += " g=" + groupID.toString() + ",r=" + groupsForUser[groupID].toString(); + } + qDebug() << "| " << userName << line; + } } diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index f59d76bd1e..c4d70baf21 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -30,7 +30,8 @@ const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions"; const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions"; const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens"; -using GroupByUUIDKey = QPair; +using GroupByUUIDKey = QPair; // groupID, rankID + class DomainServerSettingsManager : public QObject { Q_OBJECT @@ -58,32 +59,34 @@ public: QStringList getAllNames() const; // these give access to permissions for specific groups from the domain-server settings page - bool havePermissionsForGroup(const QString& groupName, int rank) const { - return _groupPermissions.contains(groupName, rank); + bool havePermissionsForGroup(const QString& groupName, QUuid rankID) const { + return _groupPermissions.contains(groupName, rankID); } - NodePermissions getPermissionsForGroup(const QString& groupName, int rank) const; - NodePermissions getPermissionsForGroup(const QUuid& groupID, int rank) const; + NodePermissions getPermissionsForGroup(const QString& groupName, QUuid rankID) const; + NodePermissions getPermissionsForGroup(const QUuid& groupID, QUuid rankID) const; // these remove permissions from users in certain groups - bool haveForbiddensForGroup(const QString& groupName, int rank) const { return _groupForbiddens.contains(groupName, rank); } - NodePermissions getForbiddensForGroup(const QString& groupName, int rank) const; - NodePermissions getForbiddensForGroup(const QUuid& groupID, int rank) const; + bool haveForbiddensForGroup(const QString& groupName, QUuid rankID) const { + return _groupForbiddens.contains(groupName, rankID); + } + NodePermissions getForbiddensForGroup(const QString& groupName, QUuid rankID) const; + NodePermissions getForbiddensForGroup(const QUuid& groupID, QUuid rankID) const; QStringList getAllKnownGroupNames(); bool setGroupID(const QString& groupName, const QUuid& groupID); + GroupRank getGroupRank(QUuid groupID, QUuid rankID) { return _groupRanks[groupID][rankID]; } QList getGroupIDs(); QList getBlacklistGroupIDs(); // these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api void clearGroupMemberships(const QString& name) { _groupMembership[name].clear(); } - void recordGroupMembership(const QString& name, const QUuid groupID, int rank); - int isGroupMember(const QString& name, const QUuid& groupID); // returns rank or -1 if not a member + void recordGroupMembership(const QString& name, const QUuid groupID, QUuid rankID); + QUuid isGroupMember(const QString& name, const QUuid& groupID); // returns rank or -1 if not a member // calls http api to refresh group information void apiRefreshGroupInformation(); - signals: void updateNodePermissions(); @@ -138,11 +141,10 @@ private: QHash _groupNames; // keep track of group-id to group-name mappings // remember the responses to api/v1/groups/%1/ranks - QHash> _groupRanks; // QHash> - QHash _groupRanksLastFetched; // when did we last update _groupRanks + QHash> _groupRanks; // QHash> // keep track of answers to api queries about which users are in which groups - QHash> _groupMembership; // QHash> + QHash> _groupMembership; // QHash> void debugDumpGroupsState(); diff --git a/libraries/networking/src/GroupRank.h b/libraries/networking/src/GroupRank.h new file mode 100644 index 0000000000..89675684d8 --- /dev/null +++ b/libraries/networking/src/GroupRank.h @@ -0,0 +1,36 @@ +// +// GroupRank.h +// libraries/networking/src/ +// +// Created by Seth Alves on 2016-7-21. +// 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_GroupRank_h +#define hifi_GroupRank_h + +class GroupRank { +public: + GroupRank() : id(QUuid()), order(-1), name(""), membersCount(-1) {} + GroupRank(QUuid id, unsigned int order, QString name, unsigned int membersCount) : + id(id), order(order), name(name), membersCount(membersCount) {} + + QUuid id; + int order; + QString name; + int membersCount; +}; + +inline bool operator==(const GroupRank& lhs, const GroupRank& rhs) { + return + lhs.id == rhs.id && + lhs.order == rhs.order && + lhs.name == rhs.name && + lhs.membersCount == rhs.membersCount; +} +inline bool operator!=(const GroupRank& lhs, const GroupRank& rhs) { return !(lhs == rhs); } + +#endif // hifi_GroupRank_h diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index 29b05fc837..c73ca122ff 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -32,8 +32,8 @@ NodePermissions::NodePermissions(QMap perms) { _groupIDSet = true; } } - if (perms.contains("rank")) { - _rank = perms["rank"].toInt(); + if (perms.contains("rank_id")) { + _rankID = QUuid(perms["rank_id"].toString()); } permissions = NodePermissions::Permissions(); @@ -46,14 +46,15 @@ NodePermissions::NodePermissions(QMap perms) { Permission::canConnectPastMaxCapacity : Permission::none; } -QVariant NodePermissions::toVariant(QVector rankNames) { +QVariant NodePermissions::toVariant(QHash groupRanks) { QMap values; values["permissions_id"] = _id; if (_groupIDSet) { values["group_id"] = _groupID; - values["rank"] = _rank; - if (rankNames.size() > _rank) { - values["rank_name"] = rankNames[_rank]; + if (groupRanks.contains(_rankID)) { + values["rank_id"] = _rankID; + values["rank_name"] = groupRanks[_rankID].name; + values["rank_order"] = groupRanks[_rankID].order; } } values["id_can_connect"] = can(Permission::canConnectToDomain); @@ -147,7 +148,7 @@ QDataStream& operator>>(QDataStream& in, NodePermissions& perms) { QDebug operator<<(QDebug debug, const NodePermissions& perms) { debug.nospace() << "[permissions: " << perms.getID() << "/" << perms.getVerifiedUserName() << " -- "; - debug.nospace() << "rank=" << perms.getRank() + debug.nospace() << "rank=" << perms.getRankID() << ", groupID=" << perms.getGroupID() << "/" << (perms.isGroup() ? "y" : "n"); if (perms.can(NodePermissions::Permission::canConnectToDomain)) { debug << " connect"; diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 541f90aee8..6d12c4cf7e 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -18,21 +18,24 @@ #include #include +#include "GroupRank.h" + class NodePermissions; using NodePermissionsPointer = std::shared_ptr; -using NodePermissionsKey = QPair; -using NodePermissionsKeyList = QList>; +using NodePermissionsKey = QPair; // name, rankID +using NodePermissionsKeyList = QList>; + class NodePermissions { public: - NodePermissions() { _id = QUuid::createUuid().toString(); _rank = 0; } - NodePermissions(const QString& name) { _id = name.toLower(); _rank = 0; } - NodePermissions(const NodePermissionsKey& key) { _id = key.first.toLower(); _rank = key.second; } + NodePermissions() { _id = QUuid::createUuid().toString(); _rankID = QUuid(); } + NodePermissions(const QString& name) { _id = name.toLower(); _rankID = QUuid(); } + NodePermissions(const NodePermissionsKey& key) { _id = key.first.toLower(); _rankID = key.second; } NodePermissions(QMap perms); QString getID() const { return _id; } // a user-name or a group-name, not verified - int getRank() const { return _rank; } - NodePermissionsKey getKey() const { return NodePermissionsKey(_id, _rank); } + QUuid getRankID() const { return _rankID; } + NodePermissionsKey getKey() const { return NodePermissionsKey(_id, _rankID); } // the _id member isn't authenticated/verified and _username is. void setVerifiedUserName(QString userName) { _verifiedUserName = userName.toLower(); } @@ -63,7 +66,7 @@ public: Q_DECLARE_FLAGS(Permissions, Permission) Permissions permissions; - QVariant toVariant(QVector rankNames = QVector()); + QVariant toVariant(QHash groupRanks = QHash()); void setAll(bool value); @@ -81,7 +84,7 @@ public: protected: QString _id; - int _rank { 0 }; // 0 unless this is for a group + QUuid _rankID { QUuid() }; // 0 unless this is for a group QString _verifiedUserName; bool _groupIDSet { false }; @@ -107,7 +110,7 @@ public: bool contains(const NodePermissionsKey& key) const { return _data.contains(NodePermissionsKey(key.first.toLower(), key.second)); } - bool contains(const QString& keyFirst, int keySecond) const { + bool contains(const QString& keyFirst, QUuid keySecond) const { return _data.contains(NodePermissionsKey(keyFirst.toLower(), keySecond)); } QList keys() const { return _data.keys(); } From 6ec8aff64539ca870b57ff3a9ba4212599326679 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 22 Jul 2016 12:02:17 -0700 Subject: [PATCH 31/98] remove group-related debug prints --- domain-server/src/DomainGatekeeper.cpp | 42 +++++++++++-------- .../src/DomainServerSettingsManager.cpp | 9 ---- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 08fe40faff..6923038ab0 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -126,26 +126,36 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, if (isLocalUser) { userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLocalhost); - qDebug() << "user-permissions: is local user, so:" << userPerms; + #ifdef WANT_DEBUG + qDebug() << "| user-permissions: is local user, so:" << userPerms; + #endif } if (verifiedUsername.isEmpty()) { userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous); - qDebug() << "user-permissions: unverified or no username, so:" << userPerms; + #ifdef WANT_DEBUG + qDebug() << "| user-permissions: unverified or no username, so:" << userPerms; + #endif } else { userPerms.setVerifiedUserName(verifiedUsername); if (_server->_settingsManager.havePermissionsForName(verifiedUsername)) { userPerms = _server->_settingsManager.getPermissionsForName(verifiedUsername); - qDebug() << "user-permissions: specific user matches, so:" << userPerms; + #ifdef WANT_DEBUG + qDebug() << "| user-permissions: specific user matches, so:" << userPerms; + #endif } else { // they are logged into metaverse, but we don't have specific permissions for them. userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn); - qDebug() << "user-permissions: user is logged-into metaverse, so:" << userPerms; + #ifdef WANT_DEBUG + qDebug() << "| user-permissions: user is logged-into metaverse, so:" << userPerms; + #endif // if this user is a friend of the domain-owner, give them friend's permissions if (_domainOwnerFriends.contains(verifiedUsername)) { userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameFriends); - qDebug() << "user-permissions: user is friends with domain-owner, so:" << userPerms; + #ifdef WANT_DEBUG + qDebug() << "| user-permissions: user is friends with domain-owner, so:" << userPerms; + #endif } // if this user is a known member of a group, give them the implied permissions @@ -155,13 +165,14 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, userPerms |= _server->_settingsManager.getPermissionsForGroup(groupID, rankID); GroupRank rank = _server->_settingsManager.getGroupRank(groupID, rankID); - qDebug() << "user-permissions: user is in group:" << groupID << " rank:" << rank.name << "so:" << userPerms; + #ifdef WANT_DEBUG + qDebug() << "| user-permissions: user is in group:" << groupID << " rank:" + << rank.name << "so:" << userPerms; + #endif } } // if this user is a known member of a blacklist group, remove the implied permissions - qDebug() << "------------------ checking blacklists ----------------------"; - qDebug() << _server->_settingsManager.getBlacklistGroupIDs(); foreach (QUuid groupID, _server->_settingsManager.getBlacklistGroupIDs()) { QUuid rankID = _server->_settingsManager.isGroupMember(verifiedUsername, groupID); if (rankID != QUuid()) { @@ -170,17 +181,19 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, userPerms &= ~_server->_settingsManager.getForbiddensForGroup(groupID, rankID); GroupRank rank = _server->_settingsManager.getGroupRank(groupID, rankID); - qDebug() << "user-permissions: user is in blacklist group:" << groupID << " rank:" << rank.name + #ifdef WANT_DEBUG + qDebug() << "| user-permissions: user is in blacklist group:" << groupID << " rank:" << rank.name << "so:" << userPerms; + #endif } - } else { - qDebug() << groupID << verifiedUsername << "is not member."; } } } } - qDebug() << "user-permissions: final:" << userPerms; + #ifdef WANT_DEBUG + qDebug() << "| user-permissions: final:" << userPerms; + #endif return userPerms; } @@ -781,12 +794,7 @@ void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) // "status":"success" // } - QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); - - qDebug() << "********* getIsGroupMember api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); - - if (jsonObject["status"].toString() == "success") { QJsonObject data = jsonObject["data"].toObject(); QJsonObject groups = data["groups"].toObject(); diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index bd429029ed..665ce222ed 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -31,9 +31,6 @@ #include "DomainServerSettingsManager.h" -#define WANT_DEBUG 1 - - const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json"; const QString DESCRIPTION_SETTINGS_KEY = "settings"; @@ -598,8 +595,6 @@ bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() { } } - debugDumpGroupsState(); - return changed; } @@ -764,10 +759,6 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection rootObject[SETTINGS_RESPONSE_DESCRIPTION_KEY] = _descriptionArray; rootObject[SETTINGS_RESPONSE_VALUE_KEY] = responseObjectForType("", true); rootObject[SETTINGS_RESPONSE_LOCKED_VALUES_KEY] = QJsonDocument::fromVariant(_configMap.getMasterConfig()).object(); - - - qDebug() << QJsonDocument(rootObject).toJson(QJsonDocument::Indented); - connection->respond(HTTPConnection::StatusCode200, QJsonDocument(rootObject).toJson(), "application/json"); } From 8e5262872e41af10bd935989f89fb8f25405940e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 22 Jul 2016 14:00:46 -0700 Subject: [PATCH 32/98] fix bug that was losing verified status for users with permissions specific to their account --- domain-server/src/DomainGatekeeper.cpp | 13 ++++++++++--- domain-server/src/DomainServerSettingsManager.h | 5 ++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 6923038ab0..718bfafdc0 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -21,6 +21,9 @@ #include "DomainServer.h" #include "DomainServerNodeData.h" +#define WANT_DEBUG 1 + + using SharedAssignmentPointer = QSharedPointer; DomainGatekeeper::DomainGatekeeper(DomainServer* server) : @@ -134,16 +137,17 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, if (verifiedUsername.isEmpty()) { userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous); #ifdef WANT_DEBUG - qDebug() << "| user-permissions: unverified or no username, so:" << userPerms; + qDebug() << "| user-permissions: unverified or no username for" << userPerms.getID() << ", so:" << userPerms; #endif } else { - userPerms.setVerifiedUserName(verifiedUsername); if (_server->_settingsManager.havePermissionsForName(verifiedUsername)) { userPerms = _server->_settingsManager.getPermissionsForName(verifiedUsername); + userPerms.setVerifiedUserName(verifiedUsername); #ifdef WANT_DEBUG qDebug() << "| user-permissions: specific user matches, so:" << userPerms; #endif } else { + userPerms.setVerifiedUserName(verifiedUsername); // they are logged into metaverse, but we don't have specific permissions for them. userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn); #ifdef WANT_DEBUG @@ -851,7 +855,6 @@ void DomainGatekeeper::getDomainOwnerFriendsListErrorCallback(QNetworkReply& req void DomainGatekeeper::refreshGroupsCache() { // if agents are connected to this domain, refresh our cached information about groups and memberships in such. - getDomainOwnerFriendsList(); int agentCount = 0; @@ -872,4 +875,8 @@ void DomainGatekeeper::refreshGroupsCache() { } updateNodePermissions(); + + #if WANT_DEBUG + _server->_settingsManager.debugDumpGroupsState(); + #endif } diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index c4d70baf21..61642d4914 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -87,6 +87,8 @@ public: // calls http api to refresh group information void apiRefreshGroupInformation(); + void debugDumpGroupsState(); + signals: void updateNodePermissions(); @@ -145,9 +147,6 @@ private: // keep track of answers to api queries about which users are in which groups QHash> _groupMembership; // QHash> - - - void debugDumpGroupsState(); }; #endif // hifi_DomainServerSettingsManager_h From 82dfdebd4669df6c58f81a4d2ec6a44dd4ec6199 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 22 Jul 2016 14:14:03 -0700 Subject: [PATCH 33/98] move permissions for specific-users below groups. turn debug-prints back off --- .../resources/describe-settings.json | 132 +++++++++--------- domain-server/src/DomainGatekeeper.cpp | 3 - 2 files changed, 66 insertions(+), 69 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index aca288c452..83c59f81a7 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -451,72 +451,6 @@ "non-deletable-row-key": "permissions_id", "non-deletable-row-values": ["localhost", "anonymous", "logged-in"] }, - { - "name": "permissions", - "type": "table", - "caption": "Permissions for Specific Users", - "can_add_new_rows": true, - - "groups": [ - { - "label": "User", - "span": 1 - }, - { - "label": "Permissions ?", - "span": 6 - } - ], - - "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": "group_permissions", "type": "table", @@ -682,6 +616,72 @@ "default": false } ] + }, + { + "name": "permissions", + "type": "table", + "caption": "Permissions for Specific Users", + "can_add_new_rows": true, + + "groups": [ + { + "label": "User", + "span": 1 + }, + { + "label": "Permissions ?", + "span": 6 + } + ], + + "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 + } + ] } ] }, diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 718bfafdc0..eb311d3aa7 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -21,9 +21,6 @@ #include "DomainServer.h" #include "DomainServerNodeData.h" -#define WANT_DEBUG 1 - - using SharedAssignmentPointer = QSharedPointer; DomainGatekeeper::DomainGatekeeper(DomainServer* server) : From f35b9350313164f0a9708f9a5787001c26a7f9d9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 22 Jul 2016 17:51:31 -0700 Subject: [PATCH 34/98] do some cleaning-up of the domain-server settings page group tables --- .../resources/describe-settings.json | 30 ++++--- .../resources/web/settings/js/settings.js | 10 ++- .../src/DomainServerSettingsManager.cpp | 82 ++++++++++++++----- .../src/DomainServerSettingsManager.h | 3 +- libraries/networking/src/NodePermissions.h | 2 + 5 files changed, 93 insertions(+), 34 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 83c59f81a7..2965276969 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -460,7 +460,7 @@ "groups": [ { "label": "Group", - "span": 5 + "span": 2 }, { "label": "Permissions ?", @@ -475,20 +475,26 @@ }, { "name": "rank_id", - "label": "Rank ID" + "label": "Rank ID", + "readonly": true, + "hidden": true }, { "name": "rank_order", - "label": "Rank Order" + "label": "Rank Order", + "readonly": true, + "hidden": true }, { "name": "rank_name", - "label": "Rank Name" + "label": "Rank Name", + "readonly": true }, { "name": "group_id", "label": "Group ID", - "readonly": true + "readonly": true, + "hidden": true }, { "name": "id_can_connect", @@ -543,7 +549,7 @@ "groups": [ { "label": "Group", - "span": 5 + "span": 2 }, { "label": "Permissions ?", @@ -558,20 +564,24 @@ }, { "name": "rank_id", - "label": "Rank ID" + "label": "Rank ID", + "hidden": true }, { "name": "rank_order", - "label": "Rank Order" + "label": "Rank Order", + "hidden": true }, { "name": "rank_name", - "label": "Rank Name" + "label": "Rank Name", + "readonly": true }, { "name": "group_id", "label": "Group ID", - "readonly": true + "readonly": true, + "hidden": true }, { "name": "id_can_connect", diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 54be33e764..df4509a1ac 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -975,7 +975,8 @@ function makeTable(setting, keypath, setting_value, isLocked) { } _.each(setting.columns, function(col) { - html += "" + col.label + "" // Data + html += "" + col.label + "" // Data }) if (!isLocked && !setting.read_only) { @@ -1027,8 +1028,9 @@ function makeTable(setting, keypath, setting_value, isLocked) { + "name='" + colName + "' value='" + (colValue || col.default || "00:00") + "' />"; } else { // Use a hidden input so that the values are posted. - html += "" - + colValue + ""; + html += "" + colValue + ""; } }) @@ -1081,7 +1083,7 @@ function makeTableInputs(setting) { + ""; } else { - html += "\ + html += "\ \ " diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 665ce222ed..9d2d4c83ee 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -324,8 +324,34 @@ void DomainServerSettingsManager::validateDescriptorsMap() { } } + +void DomainServerSettingsManager::initializeGroupPermissions(NodePermissionsMap& permissionsRows, + QString groupName, NodePermissionsPointer perms) { + // this is called when someone has used the domain-settings webpage to add a group. They type the group's name + // and give it some permissions. The domain-server asks api for the group's ranks and populates the map + // with them. Here, that initial user-entered row is removed and it's permissions are copied to all the ranks + // except owner. + + QString groupNameLower = groupName.toLower(); + + foreach (NodePermissionsKey nameKey, permissionsRows.keys()) { + if (nameKey.first.toLower() != groupNameLower) { + continue; + } + QUuid groupID = _groupIDs[groupNameLower]; + QUuid rankID = nameKey.second; + GroupRank rank = _groupRanks[groupID][rankID]; + if (rank.order == 0) { + // we don't copy the initial permissions to the owner. + continue; + } + permissionsRows[nameKey]->setAll(false); + permissionsRows[nameKey] |= perms; + } +} + void DomainServerSettingsManager::packPermissionsForMap(QString mapName, - NodePermissionsMap& agentPermissions, + NodePermissionsMap& permissionsRows, QString keyPath) { // find (or create) the "security" section of the settings map QVariant* security = valueForKeyPath(_configMap.getUserConfig(), "security"); @@ -344,7 +370,7 @@ void DomainServerSettingsManager::packPermissionsForMap(QString mapName, // convert details for each member of the subsection QVariantList* permissionsList = reinterpret_cast(permissions); (*permissionsList).clear(); - QList permissionsKeys = agentPermissions.keys(); + QList permissionsKeys = permissionsRows.keys(); // when a group is added from the domain-server settings page, the config map has a group-name with // no ID or rank. We need to leave that there until we get a valid response back from the api. @@ -353,20 +379,30 @@ void DomainServerSettingsManager::packPermissionsForMap(QString mapName, QHash groupNamesWithRanks; // note which groups have rank/ID information foreach (NodePermissionsKey userKey, permissionsKeys) { - NodePermissionsPointer perms = agentPermissions[userKey]; + NodePermissionsPointer perms = permissionsRows[userKey]; if (perms->getRankID() != QUuid()) { groupNamesWithRanks[userKey.first] = true; } } + foreach (NodePermissionsKey userKey, permissionsKeys) { + NodePermissionsPointer perms = permissionsRows[userKey]; + if (perms->isGroup()) { + QString groupName = userKey.first; + if (perms->getRankID() == QUuid() && groupNamesWithRanks.contains(groupName)) { + // copy the values from this user-added entry to the other (non-owner) ranks and remove it. + permissionsRows.remove(userKey); + initializeGroupPermissions(permissionsRows, groupName, perms); + } + } + } // convert each group-name / rank-id pair to a variant-map foreach (NodePermissionsKey userKey, permissionsKeys) { - NodePermissionsPointer perms = agentPermissions[userKey]; + if (!permissionsRows.contains(userKey)) { + continue; + } + NodePermissionsPointer perms = permissionsRows[userKey]; if (perms->isGroup()) { - if (perms->getRankID() == QUuid() && groupNamesWithRanks.contains(userKey.first)) { - // skip over the entry that was created when the user added the group. - continue; - } QHash& groupRanks = _groupRanks[perms->getGroupID()]; *permissionsList += perms->toVariant(groupRanks); } else { @@ -567,10 +603,17 @@ bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() { perms = _groupPermissions[nameKey]; } else { perms = NodePermissionsPointer(new NodePermissions(nameKey)); - perms->setGroupID(groupID); _groupPermissions[nameKey] = perms; changed = true; } + if (perms->getGroupID() != groupID) { + perms->setGroupID(groupID); + changed = true; + } + if (perms->getRankID() != rankID) { + perms->setRankID(rankID); + changed = true; + } _groupPermissionsByUUID[idKey] = perms; } } @@ -587,10 +630,17 @@ bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() { perms = _groupForbiddens[nameKey]; } else { perms = NodePermissionsPointer(new NodePermissions(nameKey)); - perms->setGroupID(groupID); _groupForbiddens[nameKey] = perms; changed = true; } + if (perms->getGroupID() != groupID) { + perms->setGroupID(groupID); + changed = true; + } + if (perms->getRankID() != rankID) { + perms->setRankID(rankID); + changed = true; + } _groupForbiddensByUUID[idKey] = perms; } } @@ -745,6 +795,7 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection QTimer::singleShot(DOMAIN_SERVER_RESTART_TIMER_MSECS, qApp, SLOT(restart())); } else { unpackPermissions(); + apiRefreshGroupInformation(); emit updateNodePermissions(); } @@ -1272,8 +1323,6 @@ void DomainServerSettingsManager::apiGetGroupRanks(const QUuid& groupID) { } void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply& requestReply) { - - // { // "data":{ // "groups":{ @@ -1420,16 +1469,11 @@ void DomainServerSettingsManager::debugDumpGroupsState() { qDebug() << "_groupRanks:"; foreach (QUuid groupID, _groupRanks.keys()) { QHash& ranksForGroup = _groupRanks[groupID]; - QString readableRanks; + qDebug() << "| " << groupID; foreach (QUuid rankID, ranksForGroup.keys()) { QString rankName = ranksForGroup[rankID].name; - if (readableRanks == "") { - readableRanks = rankName; - } else { - readableRanks += "," + rankName; - } + qDebug() << "| " << rankID << rankName; } - qDebug() << "| " << groupID << "==>" << readableRanks; } qDebug() << "_groupMembership"; diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 61642d4914..f56b1ecd21 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -125,7 +125,8 @@ private: void apiGetGroupID(const QString& groupName); void apiGetGroupRanks(const QUuid& groupID); - void packPermissionsForMap(QString mapName, NodePermissionsMap& agentPermissions, QString keyPath); + void initializeGroupPermissions(NodePermissionsMap& permissionsRows, QString groupName, NodePermissionsPointer perms); + void packPermissionsForMap(QString mapName, NodePermissionsMap& permissionsRows, QString keyPath); void packPermissions(); void unpackPermissions(); bool ensurePermissionsForGroupRanks(); diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 6d12c4cf7e..37aea6dd05 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -34,6 +34,7 @@ public: NodePermissions(QMap perms); QString getID() const { return _id; } // a user-name or a group-name, not verified + void setRankID(QUuid& rankID) { _rankID = rankID; } QUuid getRankID() const { return _rankID; } NodePermissionsKey getKey() const { return NodePermissionsKey(_id, _rankID); } @@ -116,6 +117,7 @@ public: QList keys() const { return _data.keys(); } QHash get() { return _data; } void clear() { _data.clear(); } + void remove(const NodePermissionsKey& key) { _data.remove(key); } private: QHash _data; From b3e04b53701c1a92abff1fdbea9cab51a9e1545e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 26 Jul 2016 09:35:58 -0700 Subject: [PATCH 35/98] small cleanups for permissions help text --- domain-server/resources/describe-settings.json | 8 ++++---- domain-server/resources/web/css/style.css | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 2965276969..2b7cfc9a74 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -384,7 +384,7 @@ "name": "standard_permissions", "type": "table", "label": "Domain-Wide User Permissions", - "help": "Indicate which users or groups can have which domain-wide permissions.", + "help": "Indicate which types of users can have which domain-wide permissions.", "caption": "Standard Permissions", "can_add_new_rows": false, @@ -463,7 +463,7 @@ "span": 2 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], @@ -552,7 +552,7 @@ "span": 2 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], @@ -639,7 +639,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index b9fc8b16e7..b019f59b4e 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -133,7 +133,7 @@ table .headers + .headers td { color: #222; } -table[name="security.standard_permissions"] .headers td + td, table[name="security.permissions"] .headers td + td, table[name="security.group_permissions"] .headers td + td { +#security table .headers td + td { text-align: center; } From 148793011db1b4fe0ccfa8080e49b867917530ad Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 27 Jul 2016 07:55:09 -0700 Subject: [PATCH 36/98] code review --- .../resources/describe-settings.json | 4 +- domain-server/src/DomainGatekeeper.cpp | 73 ++++++------------- domain-server/src/DomainGatekeeper.h | 4 +- .../src/DomainServerSettingsManager.cpp | 32 ++++---- libraries/networking/src/NodePermissions.h | 8 +- 5 files changed, 46 insertions(+), 75 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 2b7cfc9a74..bb067877aa 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -463,7 +463,7 @@ "span": 2 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], @@ -639,7 +639,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index eb311d3aa7..f8d63201be 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -126,37 +126,37 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, if (isLocalUser) { userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLocalhost); - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG qDebug() << "| user-permissions: is local user, so:" << userPerms; - #endif +#endif } if (verifiedUsername.isEmpty()) { userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous); - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG qDebug() << "| user-permissions: unverified or no username for" << userPerms.getID() << ", so:" << userPerms; - #endif +#endif } else { if (_server->_settingsManager.havePermissionsForName(verifiedUsername)) { userPerms = _server->_settingsManager.getPermissionsForName(verifiedUsername); userPerms.setVerifiedUserName(verifiedUsername); - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG qDebug() << "| user-permissions: specific user matches, so:" << userPerms; - #endif +#endif } else { userPerms.setVerifiedUserName(verifiedUsername); // they are logged into metaverse, but we don't have specific permissions for them. userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn); - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG qDebug() << "| user-permissions: user is logged-into metaverse, so:" << userPerms; - #endif +#endif // if this user is a friend of the domain-owner, give them friend's permissions if (_domainOwnerFriends.contains(verifiedUsername)) { userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameFriends); - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG qDebug() << "| user-permissions: user is friends with domain-owner, so:" << userPerms; - #endif +#endif } // if this user is a known member of a group, give them the implied permissions @@ -166,10 +166,10 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, userPerms |= _server->_settingsManager.getPermissionsForGroup(groupID, rankID); GroupRank rank = _server->_settingsManager.getGroupRank(groupID, rankID); - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG qDebug() << "| user-permissions: user is in group:" << groupID << " rank:" << rank.name << "so:" << userPerms; - #endif +#endif } } @@ -182,19 +182,19 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser, userPerms &= ~_server->_settingsManager.getForbiddensForGroup(groupID, rankID); GroupRank rank = _server->_settingsManager.getGroupRank(groupID, rankID); - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG qDebug() << "| user-permissions: user is in blacklist group:" << groupID << " rank:" << rank.name << "so:" << userPerms; - #endif +#endif } } } } } - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG qDebug() << "| user-permissions: final:" << userPerms; - #endif +#endif return userPerms; } @@ -529,7 +529,7 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) { // public-key request for this username is already flight, not rerequesting return; } - _inFlightPublicKeyRequests[lowerUsername] = true; + _inFlightPublicKeyRequests += lowerUsername; // even if we have a public key for them right now, request a new one in case it has just changed JSONCallbackParameters callbackParams; @@ -721,35 +721,6 @@ void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer } } -// void DomainGatekeeper::getGroupMemberships(const QString& username) { -// // loop through the groups mentioned on the settings page and ask if this user is in each. The replies -// // will be received asynchronously and permissions will be updated as the answers come in. -// QList groupIDs = _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs(); -// // TODO -- use alternative that allows checking entire group list in one call -// foreach (QUuid groupID, groupIDs) { -// if (groupID.isNull()) { -// continue; -// } -// getIsGroupMember(username, groupID); -// } -// } - -// void DomainGatekeeper::getIsGroupMember(const QString& username, const QUuid groupID) { -// JSONCallbackParameters callbackParams; -// callbackParams.jsonCallbackReceiver = this; -// callbackParams.jsonCallbackMethod = "getIsGroupMemberJSONCallback"; -// callbackParams.errorCallbackReceiver = this; -// callbackParams.errorCallbackMethod = "getIsGroupMemberErrorCallback"; - -// const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/%1/members/%2"; -// QString groupIDStr = groupID.toString().mid(1,36); -// DependencyManager::get()->sendRequest(GET_IS_GROUP_MEMBER_PATH.arg(groupIDStr).arg(username), -// AccountManagerAuth::Required, -// QNetworkAccessManager::GetOperation, callbackParams); -// } - - - void DomainGatekeeper::getGroupMemberships(const QString& username) { // loop through the groups mentioned on the settings page and ask if this user is in each. The replies // will be received asynchronously and permissions will be updated as the answers come in. @@ -839,7 +810,7 @@ void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requ QJsonArray friends = jsonObject["data"].toObject()["users"].toArray(); for (int i = 0; i < friends.size(); i++) { QString friendUserName = friends.at(i).toObject()["username"].toString(); - _domainOwnerFriends[friendUserName] = true; + _domainOwnerFriends += friendUserName; } } else { qDebug() << "getDomainOwnerFriendsList api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); @@ -859,8 +830,8 @@ void DomainGatekeeper::refreshGroupsCache() { nodeList->eachNode([&](const SharedNodePointer& node) { if (!node->getPermissions().isAssignment) { // this node is an agent - QString verifiedUserName = node->getPermissions().getVerifiedUserName(); - if (verifiedUserName != "") { + const QString& verifiedUserName = node->getPermissions().getVerifiedUserName(); + if (verifiedUserName.isEmpty()) { getGroupMemberships(verifiedUserName); } agentCount++; @@ -873,7 +844,7 @@ void DomainGatekeeper::refreshGroupsCache() { updateNodePermissions(); - #if WANT_DEBUG +#if WANT_DEBUG _server->_settingsManager.debugDumpGroupsState(); - #endif +#endif } diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index a18554338a..a10db7420d 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -102,8 +102,8 @@ private: QHash _connectionTokenHash; QHash _userPublicKeys; - QHash _inFlightPublicKeyRequests; // keep track of which we've already asked for - QHash _domainOwnerFriends; // keep track of friends of the domain owner + QSet _inFlightPublicKeyRequests; // keep track of which we've already asked for + QSet _domainOwnerFriends; // keep track of friends of the domain owner NodePermissions applyPermissionsForUser(bool isLocalUser, NodePermissions userPerms, QString verifiedUsername); void getGroupMemberships(const QString& username); diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 9d2d4c83ee..2012bf131f 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -1164,17 +1164,17 @@ QStringList DomainServerSettingsManager::getAllKnownGroupNames() { // extract all the group names from the group-permissions and group-forbiddens settings QSet result; - QHashIterator i_permissions(_groupPermissions.get()); - while (i_permissions.hasNext()) { - i_permissions.next(); - NodePermissionsKey key = i_permissions.key(); + QHashIterator i(_groupPermissions.get()); + while (i.hasNext()) { + i.next(); + NodePermissionsKey key = i.key(); result += key.first; } - QHashIterator i_forbiddens(_groupForbiddens.get()); - while (i_forbiddens.hasNext()) { - i_forbiddens.next(); - NodePermissionsKey key = i_forbiddens.key(); + QHashIterator j(_groupForbiddens.get()); + while (j.hasNext()) { + j.next(); + NodePermissionsKey key = j.key(); result += key.first; } @@ -1186,20 +1186,20 @@ bool DomainServerSettingsManager::setGroupID(const QString& groupName, const QUu _groupIDs[groupName.toLower()] = groupID; _groupNames[groupID] = groupName; - QHashIterator i_permissions(_groupPermissions.get()); - while (i_permissions.hasNext()) { - i_permissions.next(); - NodePermissionsPointer perms = i_permissions.value(); + QHashIterator i(_groupPermissions.get()); + while (i.hasNext()) { + i.next(); + NodePermissionsPointer perms = i.value(); if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) { changed = true; perms->setGroupID(groupID); } } - QHashIterator i_forbiddens(_groupForbiddens.get()); - while (i_forbiddens.hasNext()) { - i_forbiddens.next(); - NodePermissionsPointer perms = i_forbiddens.value(); + QHashIterator j(_groupForbiddens.get()); + while (j.hasNext()) { + j.next(); + NodePermissionsPointer perms = j.value(); if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) { changed = true; perms->setGroupID(groupID); diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 37aea6dd05..610d0a68aa 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -33,17 +33,17 @@ public: NodePermissions(const NodePermissionsKey& key) { _id = key.first.toLower(); _rankID = key.second; } NodePermissions(QMap perms); - QString getID() const { return _id; } // a user-name or a group-name, not verified + const QString& getID() const { return _id; } // a user-name or a group-name, not verified void setRankID(QUuid& rankID) { _rankID = rankID; } - QUuid getRankID() const { return _rankID; } + const QUuid& getRankID() const { return _rankID; } NodePermissionsKey getKey() const { return NodePermissionsKey(_id, _rankID); } // the _id member isn't authenticated/verified and _username is. void setVerifiedUserName(QString userName) { _verifiedUserName = userName.toLower(); } - QString getVerifiedUserName() const { return _verifiedUserName; } + const QString& getVerifiedUserName() const { return _verifiedUserName; } void setGroupID(QUuid groupID) { _groupID = groupID; if (!groupID.isNull()) { _groupIDSet = true; }} - QUuid getGroupID() const { return _groupID; } + const QUuid& getGroupID() const { return _groupID; } bool isGroup() const { return _groupIDSet; } bool isAssignment { false }; From bc2ded2f97c37d0b8eee668b6d0087a8120bd7ca Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 27 Jul 2016 08:46:46 -0700 Subject: [PATCH 37/98] code review --- domain-server/src/DomainGatekeeper.cpp | 31 +++++++++---- domain-server/src/DomainGatekeeper.h | 1 + .../src/DomainServerSettingsManager.cpp | 14 +++--- libraries/networking/src/GroupRank.h | 6 +-- libraries/networking/src/NodePermissions.cpp | 45 ------------------- libraries/networking/src/NodePermissions.h | 4 -- 6 files changed, 33 insertions(+), 68 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index f8d63201be..e33c2746f6 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -315,8 +315,6 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect sendConnectionTokenPacket(username, nodeConnection.senderSockAddr); // ask for their public key right now to make sure we have it requestUserPublicKey(username); - getGroupMemberships(username); // optimistically get started on group memberships - getDomainOwnerFriendsList(); return SharedNodePointer(); } @@ -326,7 +324,6 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect userPerms.setVerifiedUserName(username); verifiedUsername = username; getGroupMemberships(username); - getDomainOwnerFriendsList(); } else if (!username.isEmpty()) { // they sent us a username, but it didn't check out requestUserPublicKey(username); @@ -725,6 +722,14 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) { // loop through the groups mentioned on the settings page and ask if this user is in each. The replies // will be received asynchronously and permissions will be updated as the answers come in. + // if we've already asked, wait for the answer before asking again + QString lowerUsername = username.toLower(); + if (_inFlightGroupMembershipsRequests.contains(lowerUsername)) { + // public-key request for this username is already flight, not rerequesting + return; + } + _inFlightGroupMembershipsRequests += lowerUsername; + QJsonObject json; QSet groupIDSet; foreach (QUuid groupID, _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs()) { @@ -747,7 +752,16 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) { } - +QString extractUsernameFromGroupMembershipsReply(QNetworkReply& requestReply) { + // extract the username from the request url + QString username; + const QString GROUP_MEMBERSHIPS_URL_REGEX_STRING = "api\\/v1\\/groups\\/members\\/([A-Za-z0-9_\\.]+)"; + QRegExp usernameRegex(GROUP_MEMBERSHIPS_URL_REGEX_STRING); + if (usernameRegex.indexIn(requestReply.url().toString()) != -1) { + username = usernameRegex.cap(1); + } + return username.toLower(); +} void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) { // { @@ -781,10 +795,13 @@ void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) } else { qDebug() << "getIsGroupMember api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); } + + _inFlightGroupMembershipsRequests.remove(extractUsernameFromGroupMembershipsReply(requestReply)); } void DomainGatekeeper::getIsGroupMemberErrorCallback(QNetworkReply& requestReply) { qDebug() << "getIsGroupMember api call failed:" << requestReply.error(); + _inFlightGroupMembershipsRequests.remove(extractUsernameFromGroupMembershipsReply(requestReply)); } void DomainGatekeeper::getDomainOwnerFriendsList() { @@ -825,7 +842,6 @@ void DomainGatekeeper::refreshGroupsCache() { // if agents are connected to this domain, refresh our cached information about groups and memberships in such. getDomainOwnerFriendsList(); - int agentCount = 0; auto nodeList = DependencyManager::get(); nodeList->eachNode([&](const SharedNodePointer& node) { if (!node->getPermissions().isAssignment) { @@ -834,13 +850,10 @@ void DomainGatekeeper::refreshGroupsCache() { if (verifiedUserName.isEmpty()) { getGroupMemberships(verifiedUserName); } - agentCount++; } }); - if (agentCount > 0) { - _server->_settingsManager.apiRefreshGroupInformation(); - } + _server->_settingsManager.apiRefreshGroupInformation(); updateNodePermissions(); diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index a10db7420d..1611d14558 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -104,6 +104,7 @@ private: QHash _userPublicKeys; QSet _inFlightPublicKeyRequests; // keep track of which we've already asked for QSet _domainOwnerFriends; // keep track of friends of the domain owner + QSet _inFlightGroupMembershipsRequests; // keep track of which we've already asked for NodePermissions applyPermissionsForUser(bool isLocalUser, NodePermissions userPerms, QString verifiedUsername); void getGroupMemberships(const QString& username); diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 2012bf131f..39e3c244d8 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -346,7 +346,7 @@ void DomainServerSettingsManager::initializeGroupPermissions(NodePermissionsMap& continue; } permissionsRows[nameKey]->setAll(false); - permissionsRows[nameKey] |= perms; + *(permissionsRows[nameKey]) |= *perms; } } @@ -480,7 +480,7 @@ void DomainServerSettingsManager::unpackPermissions() { foundFriends |= (idKey == NodePermissions::standardNameFriends); if (_standardAgentPermissions.contains(idKey)) { qDebug() << "duplicate name in standard permissions table: " << id; - _standardAgentPermissions[idKey] |= perms; + *(_standardAgentPermissions[idKey]) |= *perms; needPack = true; } else { _standardAgentPermissions[idKey] = perms; @@ -494,7 +494,7 @@ void DomainServerSettingsManager::unpackPermissions() { NodePermissionsKey idKey = NodePermissionsKey(id, 0); if (_agentPermissions.contains(idKey)) { qDebug() << "duplicate name in permissions table: " << id; - _agentPermissions[idKey] |= perms; + *(_agentPermissions[idKey]) |= *perms; needPack = true; } else { _agentPermissions[idKey] = perms; @@ -508,10 +508,10 @@ void DomainServerSettingsManager::unpackPermissions() { NodePermissionsKey idKey = perms->getKey(); if (_groupPermissions.contains(idKey)) { qDebug() << "duplicate name in group permissions table: " << id; - _groupPermissions[idKey] |= perms; + *(_groupPermissions[idKey]) |= *perms; needPack = true; } else { - _groupPermissions[idKey] = perms; + *(_groupPermissions[idKey]) = *perms; } if (perms->isGroup()) { // the group-id was cached. hook-up the uuid in the uuid->group hash @@ -527,7 +527,7 @@ void DomainServerSettingsManager::unpackPermissions() { NodePermissionsKey idKey = perms->getKey(); if (_groupForbiddens.contains(idKey)) { qDebug() << "duplicate name in group forbiddens table: " << id; - _groupForbiddens[idKey] |= perms; + *(_groupForbiddens[idKey]) |= *perms; needPack = true; } else { _groupForbiddens[idKey] = perms; @@ -1367,7 +1367,7 @@ void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply& re QHash idsFromThisUpdate; for (int rankIndex = 0; rankIndex < ranks.size(); rankIndex++) { - QJsonObject rank = ranks.at(rankIndex).toObject(); + QJsonObject rank = ranks[rankIndex].toObject(); QUuid rankID = QUuid(rank["id"].toString()); int rankOrder = rank["order"].toInt(); diff --git a/libraries/networking/src/GroupRank.h b/libraries/networking/src/GroupRank.h index 89675684d8..92dadfd220 100644 --- a/libraries/networking/src/GroupRank.h +++ b/libraries/networking/src/GroupRank.h @@ -14,14 +14,14 @@ class GroupRank { public: - GroupRank() : id(QUuid()), order(-1), name(""), membersCount(-1) {} + GroupRank() {} GroupRank(QUuid id, unsigned int order, QString name, unsigned int membersCount) : id(id), order(order), name(name), membersCount(membersCount) {} QUuid id; - int order; + int order { -1 }; QString name; - int membersCount; + int membersCount { -1 }; }; inline bool operator==(const GroupRank& lhs, const GroupRank& rhs) { diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index c73ca122ff..a815884dc5 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -77,47 +77,11 @@ NodePermissions& NodePermissions::operator|=(const NodePermissions& rhs) { permissions |= rhs.permissions; return *this; } -NodePermissions& NodePermissions::operator|=(const NodePermissionsPointer& rhs) { - if (rhs) { - *this |= *rhs.get(); - } - return *this; -} -NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs) { - if (lhs && rhs) { - *lhs.get() |= rhs; - } - return lhs; -} -NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, NodePermissions::Permission rhs) { - if (lhs) { - lhs.get()->permissions |= rhs; - } - return lhs; -} NodePermissions& NodePermissions::operator&=(const NodePermissions& rhs) { permissions &= rhs.permissions; return *this; } -NodePermissions& NodePermissions::operator&=(const NodePermissionsPointer& rhs) { - if (rhs) { - *this &= *rhs.get(); - } - return *this; -} -NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs) { - if (lhs && rhs) { - *lhs.get() &= rhs; - } - return lhs; -} -NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, NodePermissions::Permission rhs) { - if (lhs) { - lhs.get()->permissions &= rhs; - } - return lhs; -} NodePermissions NodePermissions::operator~() { NodePermissions result = *this; @@ -125,15 +89,6 @@ NodePermissions NodePermissions::operator~() { return result; } -NodePermissionsPointer operator~(NodePermissionsPointer& lhs) { - if (lhs) { - NodePermissionsPointer result { new NodePermissions }; - (*result.get()) = ~(*lhs.get()); - return result; - } - return lhs; -} - QDataStream& operator<<(QDataStream& out, const NodePermissions& perms) { out << (uint)perms.permissions; return out; diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 610d0a68aa..07b47037b8 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -72,9 +72,7 @@ public: void setAll(bool value); NodePermissions& operator|=(const NodePermissions& rhs); - NodePermissions& operator|=(const NodePermissionsPointer& rhs); NodePermissions& operator&=(const NodePermissions& rhs); - NodePermissions& operator&=(const NodePermissionsPointer& rhs); NodePermissions operator~(); friend QDataStream& operator<<(QDataStream& out, const NodePermissions& perms); friend QDataStream& operator>>(QDataStream& in, NodePermissions& perms); @@ -128,8 +126,6 @@ const NodePermissions DEFAULT_AGENT_PERMISSIONS; QDebug operator<<(QDebug debug, const NodePermissions& perms); QDebug operator<<(QDebug debug, const NodePermissionsPointer& perms); -NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs); -NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, NodePermissions::Permission rhs); NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs); NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, NodePermissions::Permission rhs); NodePermissionsPointer operator~(NodePermissionsPointer& lhs); From a2d35df34e4f47c4cbaea03ea7f75ccb811190b1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 27 Jul 2016 09:57:57 -0700 Subject: [PATCH 38/98] fix a couple of mistakes made during code-review response --- domain-server/src/DomainGatekeeper.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index e33c2746f6..891c181c7c 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -315,6 +315,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect sendConnectionTokenPacket(username, nodeConnection.senderSockAddr); // ask for their public key right now to make sure we have it requestUserPublicKey(username); + getGroupMemberships(username); // optimistically get started on group memberships return SharedNodePointer(); } @@ -847,7 +848,7 @@ void DomainGatekeeper::refreshGroupsCache() { if (!node->getPermissions().isAssignment) { // this node is an agent const QString& verifiedUserName = node->getPermissions().getVerifiedUserName(); - if (verifiedUserName.isEmpty()) { + if (!verifiedUserName.isEmpty()) { getGroupMemberships(verifiedUserName); } } From d69d5bff28f667c91284f0104c36be5d95d08db5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 27 Jul 2016 10:06:02 -0700 Subject: [PATCH 39/98] remove redundant call to userPerms.setVerifiedUserName --- domain-server/src/DomainGatekeeper.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 891c181c7c..cab3206031 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -322,7 +322,6 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect QString verifiedUsername; if (!username.isEmpty() && verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { // they sent us a username and the signature verifies it - userPerms.setVerifiedUserName(username); verifiedUsername = username; getGroupMemberships(username); } else if (!username.isEmpty()) { From 8663b260f6b6cdac938404d3ab86b0ab025112c2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 27 Jul 2016 10:14:18 -0700 Subject: [PATCH 40/98] code review -- applyPermissionsForUser to setPermissionsForUser, don't take input permissions as an argument --- domain-server/src/DomainGatekeeper.cpp | 14 +++++++------- domain-server/src/DomainGatekeeper.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index cab3206031..476b99889b 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -120,8 +120,8 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer(); limitedNodeList->eachNode([this, limitedNodeList, &nodesToKill](const SharedNodePointer& node){ // the id and the username in NodePermissions will often be the same, but id is set before - // authentication and username is only set once they user's key has been confirmed. - QString username = node->getPermissions().getVerifiedUserName(); - NodePermissions userPerms(NodePermissionsKey(username, 0)); + // authentication and verifiedUsername is only set once they user's key has been confirmed. + QString verifiedUsername = node->getPermissions().getVerifiedUserName(); + NodePermissions userPerms(NodePermissionsKey(verifiedUsername, 0)); if (node->getPermissions().isAssignment) { // this node is an assignment-client @@ -224,7 +224,7 @@ void DomainGatekeeper::updateNodePermissions() { const QHostAddress& addr = node->getLocalSocket().getAddress(); bool isLocalUser = (addr == limitedNodeList->getLocalSockAddr().getAddress() || addr == QHostAddress::LocalHost); - userPerms = applyPermissionsForUser(isLocalUser, userPerms, username); + userPerms = setPermissionsForUser(isLocalUser, verifiedUsername); } node->setPermissions(userPerms); @@ -330,7 +330,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect return SharedNodePointer(); } - userPerms = applyPermissionsForUser(isLocalUser, userPerms, verifiedUsername); + userPerms = setPermissionsForUser(isLocalUser, verifiedUsername); if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) { sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.", diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 1611d14558..12697b8f3b 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -106,7 +106,7 @@ private: QSet _domainOwnerFriends; // keep track of friends of the domain owner QSet _inFlightGroupMembershipsRequests; // keep track of which we've already asked for - NodePermissions applyPermissionsForUser(bool isLocalUser, NodePermissions userPerms, QString verifiedUsername); + NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername); void getGroupMemberships(const QString& username); // void getIsGroupMember(const QString& username, const QUuid groupID); void getDomainOwnerFriendsList(); From 3a3b1489c6cfa29631867ff1e5c7d11be3f145e7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 27 Jul 2016 11:46:52 -0700 Subject: [PATCH 41/98] code review --- .../src/DomainServerSettingsManager.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 39e3c244d8..a02b19f9fd 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -701,19 +701,17 @@ NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QString if (_groupForbiddens.contains(groupRankKey)) { return *(_groupForbiddens[groupRankKey].get()); } - NodePermissions nullForbiddens; - // XXX should this be setAll(true) ? - nullForbiddens.setAll(false); - return nullForbiddens; + NodePermissions allForbiddens; + allForbiddens.setAll(true); + return allForbiddens; } NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid& groupID, QUuid rankID) const { GroupByUUIDKey byUUIDKey = GroupByUUIDKey(groupID, rankID); if (!_groupForbiddensByUUID.contains(byUUIDKey)) { - NodePermissions nullForbiddens; - // XXX should this be setAll(true) ? - nullForbiddens.setAll(false); - return nullForbiddens; + NodePermissions allForbiddens; + allForbiddens.setAll(true); + return allForbiddens; } NodePermissionsKey groupKey = _groupForbiddensByUUID[byUUIDKey]->getKey(); @@ -1104,11 +1102,6 @@ bool permissionVariantLessThan(const QVariant &v1, const QVariant &v2) { return v1.toString() < v2.toString(); } - // if (m1.contains("rank_name") && m2.contains("rank_name") && - // m1["permissions_id"].toString() == m2["permissions_id"].toString()) { - // return m1["rank_name"].toString() < m2["rank_name"].toString(); - // } - if (m1.contains("rank_order") && m2.contains("rank_order") && m1["permissions_id"].toString() == m2["permissions_id"].toString()) { return m1["rank_order"].toInt() < m2["rank_order"].toInt(); From 390ee9aaeb428d4e909e70fe1e01040c06a84085 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 27 Jul 2016 14:03:32 -0700 Subject: [PATCH 42/98] clean up some code, add some debugging prints --- domain-server/src/DomainGatekeeper.cpp | 58 ++++++++++++++++---------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 476b99889b..946e855d0d 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -310,24 +310,30 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect bool isLocalUser = (senderHostAddress == limitedNodeList->getLocalSockAddr().getAddress() || senderHostAddress == QHostAddress::LocalHost); - if (!username.isEmpty() && usernameSignature.isEmpty()) { - // user is attempting to prove their identity to us, but we don't have enough information - sendConnectionTokenPacket(username, nodeConnection.senderSockAddr); - // ask for their public key right now to make sure we have it - requestUserPublicKey(username); - getGroupMemberships(username); // optimistically get started on group memberships - return SharedNodePointer(); - } - - QString verifiedUsername; - if (!username.isEmpty() && verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { - // they sent us a username and the signature verifies it - verifiedUsername = username; - getGroupMemberships(username); - } else if (!username.isEmpty()) { - // they sent us a username, but it didn't check out - requestUserPublicKey(username); - return SharedNodePointer(); + QString verifiedUsername; // if this remains empty, consider this an anonymous connection attempt + if (!username.isEmpty()) { + if (usernameSignature.isEmpty()) { + // user is attempting to prove their identity to us, but we don't have enough information + sendConnectionTokenPacket(username, nodeConnection.senderSockAddr); + // ask for their public key right now to make sure we have it + requestUserPublicKey(username); + getGroupMemberships(username); // optimistically get started on group memberships +#ifdef WANT_DEBUG + qDebug() << "stalling login because we have no username-signature:" << username; +#endif + return SharedNodePointer(); + } else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { + // they sent us a username and the signature verifies it + getGroupMemberships(username); + verifiedUsername = username; + } else { + // they sent us a username, but it didn't check out + requestUserPublicKey(username); +#ifdef WANT_DEBUG + qDebug() << "stalling login because signature verification failed:" << username; +#endif + return SharedNodePointer(); + } } userPerms = setPermissionsForUser(isLocalUser, verifiedUsername); @@ -335,6 +341,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) { sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.", nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::TooManyUsers); +#ifdef WANT_DEBUG + qDebug() << "stalling login due to permissions:" << username; +#endif return SharedNodePointer(); } @@ -342,6 +351,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect // we can't allow this user to connect because we are at max capacity sendConnectionDeniedPacket("Too many connected users.", nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::TooManyUsers); +#ifdef WANT_DEBUG + qDebug() << "stalling login due to max capacity:" << username; +#endif return SharedNodePointer(); } @@ -355,10 +367,8 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect // we have a node that already has these exact sockets - this occurs if a node // is unable to connect to the domain hintNodeID = node->getUUID(); - return false; } - return true; }); @@ -378,6 +388,10 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect nodeData->addOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY, uuidStringWithoutCurlyBraces(newNode->getUUID()), username); +#ifdef WANT_DEBUG + qDebug() << "accepting login:" << username; +#endif + return newNode; } @@ -417,7 +431,7 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, const HifiSockAddr& senderSockAddr) { // it's possible this user can be allowed to connect, but we need to check their username signature - QByteArray publicKeyArray = _userPublicKeys.value(username); + QByteArray publicKeyArray = _userPublicKeys.value(username.toLower()); const QUuid& connectionToken = _connectionTokenHash.value(username.toLower()); @@ -568,7 +582,7 @@ void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) { const QString JSON_DATA_KEY = "data"; const QString JSON_PUBLIC_KEY_KEY = "public_key"; - _userPublicKeys[username] = + _userPublicKeys[username.toLower()] = QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); } From cd2f61c90c467fa18853b6f2d362880377defa3a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 27 Jul 2016 14:03:51 -0700 Subject: [PATCH 43/98] if logging in, send username signature even when connecting to localhost --- libraries/networking/src/NodeList.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index a73537aad0..05b5532560 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -283,12 +283,7 @@ void NodeList::sendDomainServerCheckIn() { auto accountManager = DependencyManager::get(); const QUuid& connectionToken = _domainHandler.getConnectionToken(); - // we assume that we're on the same box as the DS if it has the same local address and - // it didn't present us with a connection token to use for username signature - bool localhostDomain = _domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost - || (_domainHandler.getSockAddr().getAddress() == _localSockAddr.getAddress() && connectionToken.isNull()); - - bool requiresUsernameSignature = !_domainHandler.isConnected() && !connectionToken.isNull() && !localhostDomain; + bool requiresUsernameSignature = !_domainHandler.isConnected() && !connectionToken.isNull(); if (requiresUsernameSignature && !accountManager->getAccountInfo().hasPrivateKey()) { qWarning() << "A keypair is required to present a username signature to the domain-server" From c1b86fe3d2309d9f344520001f9360d0ef6fa49a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 28 Jul 2016 16:16:11 -0700 Subject: [PATCH 44/98] Update progress.js to only track total number of downloads --- scripts/system/progress.js | 78 +++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/scripts/system/progress.js b/scripts/system/progress.js index c6537eef52..a072378dd6 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -39,6 +39,7 @@ background2D = {}, bar2D = {}, SCALE_2D = 0.35, // Scale the SVGs for 2D display. + //SCALE_2D = 1.0, // Scale the SVGs for 2D display. background3D = {}, bar3D = {}, PROGRESS_3D_DIRECTION = 0.0, // Degrees from avatar orientation. @@ -96,19 +97,41 @@ }); } + var maxSeen = 0; + var bestRawProgress = 0; + var wasActive = false; function onDownloadInfoChanged(info) { var i; // Update raw progress value if (info.downloading.length + info.pending === 0) { + wasActive = false; rawProgress = 100; + bestRawProgress = 100; } else { - rawProgress = 0; - for (i = 0; i < info.downloading.length; i += 1) { - rawProgress += info.downloading[i]; + var count = info.downloading.length + info.pending; + if (!wasActive) { + wasActive = true; + if (count > maxSeen) { + maxSeen = count; + } + bestRawProgress = 0; + } + //for (i = 0; i < info.downloading.length; i += 1) { + //rawProgress += info.downloading[i]; + //} + //rawProgress = rawProgress / (info.downloading.length + info.pending); + rawProgress = ((maxSeen - count) / maxSeen) * 100; + //rawProgress += 1; + //if (rawProgress > 90) { + //rawProgress = 20 + //} + + if (rawProgress > bestRawProgress) { + bestRawProgress = rawProgress; } - rawProgress = rawProgress / (info.downloading.length + info.pending); } + //print(rawProgress, bestRawProgress, maxSeen); } function createOverlays() { @@ -126,9 +149,9 @@ bar3D.overlay = Overlays.addOverlay("image3d", { url: BAR_URL, subImage: { - x: BAR_WIDTH, + x: 0, y: 0, - width: BAR_WIDTH, + width: 480, height: BAR_HEIGHT }, scale: SCALE_3D * BAR_WIDTH, @@ -150,7 +173,7 @@ bar2D.overlay = Overlays.addOverlay("image", { imageURL: BAR_URL, subImage: { - x: BAR_WIDTH, + x: 0, y: 0, width: BAR_WIDTH, height: BAR_HEIGHT @@ -168,16 +191,30 @@ Overlays.deleteOverlay(isOnHMD ? bar3D.overlay : bar2D.overlay); } + var b = 0; function update() { + /* + maxSeen = 100; + b++; + rawProgress = b; + if (rawProgress == 100) { + b = 0 + } + bestRawProgress = rawProgress; + print(rawProgress, bestRawProgress); + */ + + //print(rawProgress, bestRawProgress, maxSeen); var viewport, eyePosition, avatarOrientation; - if (isOnHMD !== HMD.active) { - deleteOverlays(); - isOnHMD = !isOnHMD; - createOverlays(); - } + hmdActive = HMD.active; + //if (isOnHMD !== HMD.active) { + //deleteOverlays(); + //isOnHMD = !isOnHMD; + //createOverlays(); + //} // Calculate progress value to display if (rawProgress === 0 && displayProgress <= DISPLAY_PROGRESS_MINOR_MAXIMUM) { @@ -187,6 +224,7 @@ } else if (rawProgress > displayProgress) { displayProgress = Math.min(rawProgress, displayProgress + DISPLAY_PROGRESS_MAJOR_INCREMENT); } // else (rawProgress === displayProgress); do nothing. + displayProgress = bestRawProgress; // Update state if (!visible) { // Not visible because no recent downloads @@ -232,7 +270,7 @@ y: 0, width: BAR_WIDTH, height: BAR_HEIGHT - } + }, }); // Update position @@ -258,20 +296,25 @@ windowWidth = viewport.x; windowHeight = viewport.y; + var yOffset = hmdActive ? -300 : 0; + Overlays.editOverlay(background2D.overlay, { x: windowWidth / 2 - background2D.width / 2, - y: windowHeight - background2D.height - bar2D.height + y: windowHeight - background2D.height - bar2D.height + yOffset }); Overlays.editOverlay(bar2D.overlay, { x: windowWidth / 2 - bar2D.width / 2, - y: windowHeight - background2D.height - bar2D.height + (background2D.height - bar2D.height) / 2 + y: windowHeight - background2D.height - bar2D.height + (background2D.height - bar2D.height) / 2 + yOffset }); } } } } + function setProgressBar(progress) { + } + function setUp() { background2D.width = SCALE_2D * BACKGROUND_WIDTH; background2D.height = SCALE_2D * BACKGROUND_HEIGHT; @@ -302,6 +345,7 @@ setUp(); GlobalServices.downloadInfoChanged.connect(onDownloadInfoChanged); GlobalServices.updateDownloadInfo(); - Script.update.connect(update); + //Script.update.connect(update); + Script.setInterval(update, 16); Script.scriptEnding.connect(tearDown); -}()); \ No newline at end of file +}()); From 873e7b63dfe7366e487111695523d31a5b1b9a36 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 28 Jul 2016 16:26:12 -0700 Subject: [PATCH 45/98] Update progress-bar.svg --- scripts/system/assets/images/progress-bar.svg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/assets/images/progress-bar.svg b/scripts/system/assets/images/progress-bar.svg index 0df6f98686..bd35b4ce53 100644 --- a/scripts/system/assets/images/progress-bar.svg +++ b/scripts/system/assets/images/progress-bar.svg @@ -1,7 +1,7 @@ - - + viewBox="0 0 960 30" enable-background="new -159 536 960 30" xml:space="preserve"> + + From a786224297f229728af9c2dbce03ccce7b352885 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 28 Jul 2016 16:55:45 -0700 Subject: [PATCH 46/98] Add easing back to progress.js --- scripts/system/progress.js | 58 ++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/scripts/system/progress.js b/scripts/system/progress.js index a072378dd6..dcd52bce98 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -100,9 +100,11 @@ var maxSeen = 0; var bestRawProgress = 0; var wasActive = false; + var cooldown = 1000; function onDownloadInfoChanged(info) { var i; + print("PROGRESS: DOwnload info changed ", info.downloading.length, info.pending, maxSeen); // Update raw progress value if (info.downloading.length + info.pending === 0) { wasActive = false; @@ -112,26 +114,33 @@ var count = info.downloading.length + info.pending; if (!wasActive) { wasActive = true; - if (count > maxSeen) { - maxSeen = count; - } + maxSeen = count; bestRawProgress = 0; + rawProgress = 0; + cooldown = 2000; + displayProgress = 0; } - //for (i = 0; i < info.downloading.length; i += 1) { - //rawProgress += info.downloading[i]; - //} - //rawProgress = rawProgress / (info.downloading.length + info.pending); - rawProgress = ((maxSeen - count) / maxSeen) * 100; - //rawProgress += 1; - //if (rawProgress > 90) { - //rawProgress = 20 - //} + if (count > maxSeen) { + maxSeen = count; + } + if (cooldown < 0) { + print("PROGRESS: OUT OF COOLDOWN"); + //for (i = 0; i < info.downloading.length; i += 1) { + //rawProgress += info.downloading[i]; + //} + //rawProgress = rawProgress / (info.downloading.length + info.pending); + rawProgress = ((maxSeen - count) / maxSeen) * 100; + //rawProgress += 1; + //if (rawProgress > 90) { + //rawProgress = 20 + //} - if (rawProgress > bestRawProgress) { - bestRawProgress = rawProgress; + if (rawProgress > bestRawProgress) { + bestRawProgress = rawProgress; + } } } - //print(rawProgress, bestRawProgress, maxSeen); + print("PROGRESS:", rawProgress, bestRawProgress, maxSeen); } function createOverlays() { @@ -193,6 +202,7 @@ var b = 0; function update() { + cooldown -= 16; /* maxSeen = 100; b++; @@ -216,6 +226,7 @@ //createOverlays(); //} + /* // Calculate progress value to display if (rawProgress === 0 && displayProgress <= DISPLAY_PROGRESS_MINOR_MAXIMUM) { displayProgress = Math.min(displayProgress + DISPLAY_PROGRESS_MINOR_INCREMENT, DISPLAY_PROGRESS_MINOR_MAXIMUM); @@ -224,7 +235,18 @@ } else if (rawProgress > displayProgress) { displayProgress = Math.min(rawProgress, displayProgress + DISPLAY_PROGRESS_MAJOR_INCREMENT); } // else (rawProgress === displayProgress); do nothing. - displayProgress = bestRawProgress; + //displayProgress = bestRawProgress; + //*/ + if (displayProgress < rawProgress) { + var diff = rawProgress - displayProgress; + if (diff < 0.1) { + displayProgress = rawProgress; + } else { + displayProgress += diff * 0.2; + } + } + print('PROGRESS:', displayProgress); + // Update state if (!visible) { // Not visible because no recent downloads @@ -235,7 +257,7 @@ } } else if (alphaDelta !== 0.0) { // Fading in or out if (alphaDelta > 0) { - if (displayProgress === 100) { // Was downloading but now have finished so fade out + if (rawProgress === 100) { // Was downloading but now have finished so fade out alphaDelta = ALPHA_DELTA_OUT; } } else { @@ -245,7 +267,7 @@ } } else { // Fully visible because downloading or recently so if (fadeWaitTimer === null) { - if (displayProgress === 100) { // Was downloading but have finished so fade out soon + if (rawProgress === 100) { // Was downloading but have finished so fade out soon fadeWaitTimer = Script.setTimeout(function() { alphaDelta = ALPHA_DELTA_OUT; fadeTimer = Script.setInterval(fade, FADE_INTERVAL); From 58932a6806f726b2337c94166d80b567d3d7f208 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 28 Jul 2016 17:13:05 -0700 Subject: [PATCH 47/98] Add reset of progress bar when domain changes --- scripts/system/progress.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts/system/progress.js b/scripts/system/progress.js index dcd52bce98..660dfc4b98 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -96,6 +96,15 @@ visible: visible }); } + function resetProgress() { + wasActive = true; + bestRawProgress = 0; + rawProgress = 0; + cooldown = 1000; + displayProgress = 0; + } + + Window.domainChanged.connect(resetProgress); var maxSeen = 0; var bestRawProgress = 0; @@ -113,12 +122,8 @@ } else { var count = info.downloading.length + info.pending; if (!wasActive) { - wasActive = true; + resetProgress(); maxSeen = count; - bestRawProgress = 0; - rawProgress = 0; - cooldown = 2000; - displayProgress = 0; } if (count > maxSeen) { maxSeen = count; @@ -242,7 +247,7 @@ if (diff < 0.1) { displayProgress = rawProgress; } else { - displayProgress += diff * 0.2; + displayProgress += diff * 0.1; } } print('PROGRESS:', displayProgress); From 3234de9f9279323d2fdfa8718a58c45a14329f39 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 27 Jul 2016 11:59:18 -0700 Subject: [PATCH 48/98] Add category headers to groups --- .../resources/describe-settings.json | 22 ++-- domain-server/resources/web/css/style.css | 4 +- .../resources/web/settings/js/settings.js | 116 +++++++++++++----- 3 files changed, 101 insertions(+), 41 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index bb067877aa..c1fe975624 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -455,12 +455,13 @@ "name": "group_permissions", "type": "table", "caption": "Permissions for Users in Groups", - "can_add_new_rows": true, + "categorize_by_key": "permissions_id", + "can_add_new_categories": true, "groups": [ { - "label": "Group", - "span": 2 + "label": "Rank", + "span": 1 }, { "label": "Permissions ?", @@ -471,7 +472,9 @@ "columns": [ { "name": "permissions_id", - "label": "Group Name" + "label": "Group Name", + "readonly": true, + "hidden": true }, { "name": "rank_id", @@ -487,7 +490,7 @@ }, { "name": "rank_name", - "label": "Rank Name", + "label": "", "readonly": true }, { @@ -548,8 +551,8 @@ "groups": [ { - "label": "Group", - "span": 2 + "label": "Rank", + "span": 1 }, { "label": "Permissions ?", @@ -560,7 +563,8 @@ "columns": [ { "name": "permissions_id", - "label": "Group Name" + "label": "Group Name", + "hidden": true }, { "name": "rank_id", @@ -574,7 +578,7 @@ }, { "name": "rank_name", - "label": "Rank Name", + "label": "", "readonly": true }, { diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index b019f59b4e..87764237f5 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -20,7 +20,9 @@ body { top: 40px; } -.table .value-row td, .table .inputs td { +.table .value-row td, +.table .value-category td, +.table .inputs td { vertical-align: middle; } diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index df4509a1ac..8e5ac25d9a 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -7,10 +7,13 @@ var Settings = { TRIGGER_CHANGE_CLASS: 'trigger-change', DATA_ROW_CLASS: 'value-row', DATA_COL_CLASS: 'value-col', + DATA_CATEGORY_CLASS: 'value-category', ADD_ROW_BUTTON_CLASS: 'add-row', ADD_ROW_SPAN_CLASSES: 'glyphicon glyphicon-plus add-row', DEL_ROW_BUTTON_CLASS: 'del-row', DEL_ROW_SPAN_CLASSES: 'glyphicon glyphicon-remove del-row', + DEL_CATEGORY_BUTTON_CLASS: 'del-category', + DEL_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-remove del-category', MOVE_UP_BUTTON_CLASS: 'move-up', MOVE_UP_SPAN_CLASSES: 'glyphicon glyphicon-chevron-up move-up', MOVE_DOWN_BUTTON_CLASS: 'move-down', @@ -164,19 +167,23 @@ $(document).ready(function(){ }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.ADD_ROW_BUTTON_CLASS, function(){ - addTableRow(this); + addTableRow($(this).closest('tr')); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){ - deleteTableRow(this); + deleteTableRow($(this).closest('tr')); + }); + + $('#' + Settings.FORM_ID).on('click', '.' + Settings.DEL_CATEGORY_BUTTON_CLASS, function(){ + deleteTableCategory($(this).closest('tr')); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){ - moveTableRow(this, true); + moveTableRow($(this).closest('tr'), true); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.MOVE_DOWN_BUTTON_CLASS, function(){ - moveTableRow(this, false); + moveTableRow($(this).closest('tr'), false); }); $('#' + Settings.FORM_ID).on('keyup', function(e){ @@ -924,7 +931,6 @@ $('body').on('click', '.save-button', function(e){ function makeTable(setting, keypath, setting_value, isLocked) { var isArray = !_.has(setting, 'key'); - var isHash = !isArray; if (!isArray && setting.can_order) { setting.can_order = false; @@ -974,25 +980,60 @@ function makeTable(setting, keypath, setting_value, isLocked) { html += "" + setting.key.label + "" // Key } + var numVisibleColumns = 0; _.each(setting.columns, function(col) { + if (!col.hidden) numVisibleColumns++; html += "" + col.label + "" // Data }) if (!isLocked && !setting.read_only) { if (setting.can_order) { + numVisibleColumns++; html += ""; } - html += "" + numVisibleColumns++; + html += ""; } // populate rows in the table from existing values var row_num = 1; if (keypath.length > 0 && _.size(setting_value) > 0) { + var rowIsObject = setting.columns.length > 1; + _.each(setting_value, function(row, rowIndexOrName) { - html += "" + var categoryKey = setting.categorize_by_key; + var isCategorized = !!categoryKey; + var categoryData = ""; + if (isCategorized && isArray) { + var predicate = {}; + var categoryValue = row[categoryKey]; + predicate[categoryKey] = categoryValue; + categoryValue = rowIsObject ? row[categoryKey] : row; + categoryData = "data-category='" + categoryValue + "'"; + + if (_.findIndex(setting_value, predicate) === rowIndexOrName) { + html += + "" + + "" + + categoryValue + + "" + + ((setting.can_add_new_categories) ? ( + "" + + "" + + "" + ) : ( + "" + )) + + ""; + } + } + + html += + "" if (setting.numbered === true) { html += "" + row_num + "" @@ -1006,8 +1047,8 @@ function makeTable(setting, keypath, setting_value, isLocked) { _.each(setting.columns, function(col) { + var colValue, colName; if (isArray) { - rowIsObject = setting.columns.length > 1; colValue = rowIsObject ? row[col.name] : row; colName = keypath + "[" + rowIndexOrName + "]" + (rowIsObject ? "." + col.name : ""); } else { @@ -1019,18 +1060,25 @@ function makeTable(setting, keypath, setting_value, isLocked) { || (nonDeletableRowKey === col.name && nonDeletableRowValues.indexOf(colValue) !== -1); if (isArray && col.type === "checkbox" && col.editable) { - html += "" - + ""; + html += + "" + + "" + + ""; } else if (isArray && col.type === "time" && col.editable) { - html += "" - + ""; + html += + "" + + "" + + ""; } else { // Use a hidden input so that the values are posted. - html += "" + colValue + ""; + html += + "" + + colValue + + "" + + ""; } }) @@ -1138,9 +1186,7 @@ function badgeSidebarForDifferences(changedElement) { $("a[href='#" + panelParentID + "'] .badge").html(badgeValue); } -function addTableRow(add_glyphicon) { - var row = $(add_glyphicon).closest('tr') - +function addTableRow(row) { var table = row.parents('table') var isArray = table.data('setting-type') === 'array' @@ -1279,9 +1325,7 @@ function addTableRow(add_glyphicon) { row.parent().append(input_clone) } -function deleteTableRow(delete_glyphicon) { - var row = $(delete_glyphicon).closest('tr') - +function deleteTableRow(row) { var table = $(row).closest('table') var isArray = table.data('setting-type') === 'array' @@ -1298,12 +1342,13 @@ function deleteTableRow(delete_glyphicon) { row.remove() } else { // this is the last row, we can't remove it completely since we need to post an empty array - - row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS) - row.addClass('empty-array-row') - - row.html(""); + row + .removeClass(Settings.DATA_ROW_CLASS) + .removeClass(Settings.NEW_ROW_CLASS) + .removeAttr("data-category") + .addClass('empty-array-row') + .html(""); } } @@ -1311,9 +1356,18 @@ function deleteTableRow(delete_glyphicon) { badgeSidebarForDifferences($(table)) } -function moveTableRow(move_glyphicon, move_up) { - var row = $(move_glyphicon).closest('tr') +function deleteTableCategory(categoryHeaderRow) { + var categoryName = categoryHeaderRow.data("category"); + categoryHeaderRow + .closest("table") + .find("tr[data-category='" + categoryName + "']") + .each(function () { + deleteTableRow($(this)); + }); +} + +function moveTableRow(row, move_up) { var table = $(row).closest('table') var isArray = table.data('setting-type') === 'array' if (!isArray) { From 93f9f93b5a1f2a700ad6d3ab2e67034e3349ed99 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 27 Jul 2016 18:11:08 -0700 Subject: [PATCH 49/98] Enable adding groups --- .../resources/describe-settings.json | 2 + domain-server/resources/web/css/style.css | 13 ++ .../resources/web/settings/js/settings.js | 142 ++++++++++++++---- 3 files changed, 128 insertions(+), 29 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index c1fe975624..5b60b1a82c 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -457,6 +457,8 @@ "caption": "Permissions for Users in Groups", "categorize_by_key": "permissions_id", "can_add_new_categories": true, + "new_category_placeholder": "Add Group", + "new_category_needs_reload": true, "groups": [ { diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index 87764237f5..809e9251c4 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -3,6 +3,10 @@ body { padding-bottom: 30px; } +[hidden] { + display: none !important; +} + .table-lead .lead-line { background-color: black; } @@ -33,6 +37,15 @@ body { margin-right: auto; } +.table .value-category { + font-weight: bold; +} + +.table .value-category .message { + font-style: italic; + font-weight: normal; +} + .glyphicon-remove { font-size: 24px; } diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 8e5ac25d9a..33afc72891 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -12,6 +12,8 @@ var Settings = { ADD_ROW_SPAN_CLASSES: 'glyphicon glyphicon-plus add-row', DEL_ROW_BUTTON_CLASS: 'del-row', DEL_ROW_SPAN_CLASSES: 'glyphicon glyphicon-remove del-row', + ADD_CATEGORY_BUTTON_CLASS: 'add-category', + ADD_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-plus add-category', DEL_CATEGORY_BUTTON_CLASS: 'del-category', DEL_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-remove del-category', MOVE_UP_BUTTON_CLASS: 'move-up', @@ -174,6 +176,10 @@ $(document).ready(function(){ deleteTableRow($(this).closest('tr')); }); + $('#' + Settings.FORM_ID).on('click', '.' + Settings.ADD_CATEGORY_BUTTON_CLASS, function(){ + addTableCategory($(this).closest('tr')); + }); + $('#' + Settings.FORM_ID).on('click', '.' + Settings.DEL_CATEGORY_BUTTON_CLASS, function(){ deleteTableCategory($(this).closest('tr')); }); @@ -205,10 +211,11 @@ $(document).ready(function(){ } if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) { - sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click() + sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click(); + sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).click(); // set focus to the first input in the new row - $target.closest('table').find('tr.inputs input:first').focus() + $target.closest('table').find('tr.inputs input:first').focus(); } } @@ -1013,21 +1020,8 @@ function makeTable(setting, keypath, setting_value, isLocked) { predicate[categoryKey] = categoryValue; categoryValue = rowIsObject ? row[categoryKey] : row; categoryData = "data-category='" + categoryValue + "'"; - if (_.findIndex(setting_value, predicate) === rowIndexOrName) { - html += - "" + - "" + - categoryValue + - "" + - ((setting.can_add_new_categories) ? ( - "" + - "" + - "" - ) : ( - "" - )) + - ""; + html += makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, setting.can_add_new_categories, false); } } @@ -1043,7 +1037,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { html += "" + rowIndexOrName + "" } - var isNonDeletableRow = !setting.can_add_new_rows; + var isNonDeletableRow = !setting.can_add_new_rows && !setting.can_add_new_categories || setting.new_category_needs_reload; _.each(setting.columns, function(col) { @@ -1104,19 +1098,47 @@ function makeTable(setting, keypath, setting_value, isLocked) { } // populate inputs in the table for new values - if (!isLocked && !setting.read_only && setting.can_add_new_rows) { - html += makeTableInputs(setting) + if (!isLocked && !setting.read_only) { + if (setting.can_add_new_categories) { + html += makeTableCategoryInput(setting, numVisibleColumns); + } + if (setting.can_add_new_rows || setting.can_add_new_categories) { + html += makeTableInputs(setting); + } } html += "" return html; } +function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, canRemove, needsReload) { + var html = + "" + + "" + + "" + categoryValue + "" + + ((needsReload) ? ( + " - Reload to see contents." + ) : ( + "" + )) + + "" + + ((canRemove) ? ( + "" + + "" + + "" + ) : ( + "" + )) + + ""; + return html; +} + function makeTableInputs(setting) { - var html = "" + var html = ""; if (setting.numbered === true) { - html += "" + html += ""; } if (setting.key) { @@ -1142,12 +1164,28 @@ function makeTableInputs(setting) { html += "" } html += "" + "'>" html += "" return html } +function makeTableCategoryInput(setting, numVisibleColumns) { + var needsReload = setting.new_category_needs_reload; + var categoryKey = setting.categorize_by_key; + var placeholder = setting.new_category_placeholder ? setting.new_category_placeholder : ""; + var html = + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + return html; +} + function badgeSidebarForDifferences(changedElement) { // figure out which group this input is in var panelParentID = changedElement.closest('.panel').attr('id'); @@ -1187,10 +1225,11 @@ function badgeSidebarForDifferences(changedElement) { } function addTableRow(row) { - var table = row.parents('table') - var isArray = table.data('setting-type') === 'array' + var table = row.parents('table'); + var isArray = table.data('setting-type') === 'array'; + var keepField = row.data("keep-field"); - var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS) + var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS); if (!isArray) { // Check key spaces @@ -1307,10 +1346,12 @@ function addTableRow(row) { } else { console.log("Unknown table element") } - }) + }); - input_clone.find('input').each(function(){ - $(this).val($(this).attr('data-default')); + input_clone.children('td').each(function () { + if ($(this).attr("name") !== keepField) { + $(this).find("input").val($(this).attr('data-default')); + } }); if (isArray) { @@ -1322,7 +1363,7 @@ function addTableRow(row) { badgeSidebarForDifferences($(table)) - row.parent().append(input_clone) + row.after(input_clone) } function deleteTableRow(row) { @@ -1356,6 +1397,49 @@ function deleteTableRow(row) { badgeSidebarForDifferences($(table)) } +function addTableCategory($categoryInputRow) { + var $input = $categoryInputRow.find("input").first(); + var $rowInput = $categoryInputRow.next(".inputs").clone(); + if (!$rowInput) { + console.error("Error cloning inputs"); + } + + var needsReload = $categoryInputRow.data("needs-reload"); + var categoryKey = $categoryInputRow.data("key"); + var categoryValue = $input.prop("value"); + var width = 0; + $categoryInputRow + .children("td") + .each(function () { + width += $(this).prop("colSpan") || 1; + }); + + $input + .prop("value", "") + .focus(); + + $rowInput.find("td[name='" + categoryKey + "'] > input").first() + .prop("value", categoryValue); + $rowInput + .attr("data-category", categoryValue) + .addClass(Settings.NEW_ROW_CLASS); + + // TODO: create inputs on initial template load + + var $newCategoryRow = $(makeTableCategoryHeader(categoryKey, categoryValue, width, true, needsReload)); + $newCategoryRow.addClass(Settings.NEW_ROW_CLASS); + + $categoryInputRow + .before($newCategoryRow) + .before($rowInput); + + if (!needsReload) { + $rowInput.removeAttr("hidden"); + } else { + addTableRow($rowInput); + } +} + function deleteTableCategory(categoryHeaderRow) { var categoryName = categoryHeaderRow.data("category"); From aa909a13663d27351d001c20bbd33b06bbe2dc12 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Thu, 28 Jul 2016 13:47:21 -0700 Subject: [PATCH 50/98] Polish category/row addition and deletion --- .../resources/describe-settings.json | 11 +- domain-server/resources/web/css/style.css | 7 +- .../resources/web/settings/js/settings.js | 140 ++++++++++-------- 3 files changed, 93 insertions(+), 65 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 5b60b1a82c..f4ceb0f432 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -457,8 +457,9 @@ "caption": "Permissions for Users in Groups", "categorize_by_key": "permissions_id", "can_add_new_categories": true, + "can_add_new_rows": false, "new_category_placeholder": "Add Group", - "new_category_needs_reload": true, + "new_category_message": "Reload to see ranks", "groups": [ { @@ -548,8 +549,12 @@ { "name": "group_forbiddens", "type": "table", - "caption": "Permissions denied to Users in Groups", - "can_add_new_rows": true, + "caption": "Permissions Denied to Users in Groups", + "categorize_by_key": "permissions_id", + "can_add_new_categories": true, + "can_add_new_rows": false, + "new_category_placeholder": "Add Blacklist Group", + "new_category_message": "Reload to see ranks", "groups": [ { diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index 809e9251c4..0a3dc3dab5 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -41,11 +41,16 @@ body { font-weight: bold; } -.table .value-category .message { +.table .value-category [message]::after { + content: attr(message); font-style: italic; font-weight: normal; } +.table .value-category.inputs { + font-weight: normal; +} + .glyphicon-remove { font-size: 24px; } diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 33afc72891..4b258fa9fb 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -42,7 +42,7 @@ var viewHelpers = { form_group = "

"; setting_value = _(values).valueForKeyPath(keypath); - if (typeof setting_value == 'undefined' || setting_value === null) { + if (_.isUndefined(setting_value) || _.isNull(setting_value)) { if (_.has(setting, 'default')) { setting_value = setting.default; } else { @@ -56,11 +56,11 @@ var viewHelpers = { } function common_attrs(extra_classes) { - extra_classes = (typeof extra_classes !== 'undefined' ? extra_classes : ""); + extra_classes = (!_.isUndefined(extra_classes) ? extra_classes : ""); return " class='" + (setting.type !== 'checkbox' ? 'form-control' : '') + " " + Settings.TRIGGER_CHANGE_CLASS + " " + extra_classes + "' data-short-name='" + setting.name + "' name='" + keypath + "' " - + "id='" + (typeof setting.html_id !== 'undefined' ? setting.html_id : keypath) + "'"; + + "id='" + (!_.isUndefined(setting.html_id) ? setting.html_id : keypath) + "'"; } if (setting.type === 'checkbox') { @@ -937,6 +937,7 @@ $('body').on('click', '.save-button', function(e){ }); function makeTable(setting, keypath, setting_value, isLocked) { + // TODO: enforce category sorting var isArray = !_.has(setting, 'key'); if (!isArray && setting.can_order) { @@ -952,9 +953,10 @@ function makeTable(setting, keypath, setting_value, isLocked) { var nonDeletableRowKey = setting["non-deletable-row-key"]; var nonDeletableRowValues = setting["non-deletable-row-values"]; - html += ""; + html += "
"; if (setting.caption) { html += "" @@ -1012,22 +1014,20 @@ function makeTable(setting, keypath, setting_value, isLocked) { _.each(setting_value, function(row, rowIndexOrName) { var categoryKey = setting.categorize_by_key; - var isCategorized = !!categoryKey; - var categoryData = ""; - if (isCategorized && isArray) { - var predicate = {}; - var categoryValue = row[categoryKey]; - predicate[categoryKey] = categoryValue; + var isCategorized = !!categoryKey && isArray; + var categoryPair = {}; + var categoryValue = ""; + if (isCategorized) { categoryValue = rowIsObject ? row[categoryKey] : row; - categoryData = "data-category='" + categoryValue + "'"; - if (_.findIndex(setting_value, predicate) === rowIndexOrName) { - html += makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, setting.can_add_new_categories, false); + categoryPair[categoryKey] = categoryValue; + if (_.findIndex(setting_value, categoryPair) === rowIndexOrName) { + html += makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, setting.can_add_new_categories, ""); } } - html += - "" + html += ""; if (setting.numbered === true) { html += "" @@ -1037,7 +1037,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { html += "" } - var isNonDeletableRow = !setting.can_add_new_rows && !setting.can_add_new_categories || setting.new_category_needs_reload; + var isNonDeletableRow = !setting.can_add_new_rows; _.each(setting.columns, function(col) { @@ -1075,7 +1075,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { ""; } - }) + }); if (!isLocked && !setting.read_only) { if (setting.can_order) { @@ -1093,6 +1093,10 @@ function makeTable(setting, keypath, setting_value, isLocked) { html += "" + if (isCategorized && setting.can_add_new_rows && _.findLastIndex(setting_value, categoryPair) === rowIndexOrName) { + html += makeTableInputs(setting, categoryPair, categoryValue); + } + row_num++ }); } @@ -1103,7 +1107,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { html += makeTableCategoryInput(setting, numVisibleColumns); } if (setting.can_add_new_rows || setting.can_add_new_categories) { - html += makeTableInputs(setting); + html += makeTableInputs(setting, {}, ""); } } html += "
" + setting.caption + "
" + row_num + "" + rowIndexOrName + "
" @@ -1111,16 +1115,11 @@ function makeTable(setting, keypath, setting_value, isLocked) { return html; } -function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, canRemove, needsReload) { +function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, canRemove, message) { var html = "" + "" + - "" + categoryValue + "" + - ((needsReload) ? ( - " - Reload to see contents." - ) : ( - "" - )) + + "" + categoryValue + "" + "" + ((canRemove) ? ( "" + @@ -1133,9 +1132,10 @@ function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, return html; } -function makeTableInputs(setting) { - var html = ""; +function makeTableInputs(setting, initialValues, categoryValue) { + var html = ""; if (setting.numbered === true) { html += ""; @@ -1148,15 +1148,21 @@ function makeTableInputs(setting) { } _.each(setting.columns, function(col) { + var defaultValue = _.has(initialValues, col.name) ? initialValues[col.name] : col.default; if (col.type === "checkbox") { - html += "" - + ""; + html += + "" + + "" + + ""; } else { - html += "\ - \ - " + html += + "" + + "" + + ""; } }) @@ -1171,12 +1177,14 @@ function makeTableInputs(setting) { } function makeTableCategoryInput(setting, numVisibleColumns) { - var needsReload = setting.new_category_needs_reload; + var canAddRows = setting.can_add_new_rows; var categoryKey = setting.categorize_by_key; - var placeholder = setting.new_category_placeholder ? setting.new_category_placeholder : ""; + var placeholder = setting.new_category_placeholder || ""; + var message = setting.new_category_message || ""; var html = - "" + - "" + + "" + + "" + "" + "" + "" + @@ -1366,35 +1374,40 @@ function addTableRow(row) { row.after(input_clone) } -function deleteTableRow(row) { - var table = $(row).closest('table') - var isArray = table.data('setting-type') === 'array' +function deleteTableRow($row) { + var $table = $row.closest('table'); + var categoryName = $row.data("category"); + var isArray = $table.data('setting-type') === 'array'; - row.empty(); + $row.empty(); if (!isArray) { - row.html(""); + $row.html(""); } else { - if (table.find('.' + Settings.DATA_ROW_CLASS).length > 1) { - updateDataChangedForSiblingRows(row) + if ($table.find('.' + Settings.DATA_ROW_CLASS + "[data-category='" + categoryName + "']").length <= 1) { + // This is the last row of the category, so delete the header + $table.find('.' + Settings.DATA_CATEGORY_CLASS + "[data-category='" + categoryName + "']").remove(); + } + + if ($table.find('.' + Settings.DATA_ROW_CLASS).length > 1) { + updateDataChangedForSiblingRows($row); // this isn't the last row - we can just remove it - row.remove() + $row.remove(); } else { // this is the last row, we can't remove it completely since we need to post an empty array - row + $row .removeClass(Settings.DATA_ROW_CLASS) .removeClass(Settings.NEW_ROW_CLASS) .removeAttr("data-category") .addClass('empty-array-row') - .html(""); } } // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated - badgeSidebarForDifferences($(table)) + badgeSidebarForDifferences($table); } function addTableCategory($categoryInputRow) { @@ -1404,7 +1417,8 @@ function addTableCategory($categoryInputRow) { console.error("Error cloning inputs"); } - var needsReload = $categoryInputRow.data("needs-reload"); + var canAddRows = $categoryInputRow.data("can-add-rows"); + var message = $categoryInputRow.data("message"); var categoryKey = $categoryInputRow.data("key"); var categoryValue = $input.prop("value"); var width = 0; @@ -1426,28 +1440,32 @@ function addTableCategory($categoryInputRow) { // TODO: create inputs on initial template load - var $newCategoryRow = $(makeTableCategoryHeader(categoryKey, categoryValue, width, true, needsReload)); + var $newCategoryRow = $(makeTableCategoryHeader(categoryKey, categoryValue, width, true, " - " + message)); $newCategoryRow.addClass(Settings.NEW_ROW_CLASS); $categoryInputRow .before($newCategoryRow) .before($rowInput); - if (!needsReload) { + if (canAddRows) { $rowInput.removeAttr("hidden"); } else { addTableRow($rowInput); } } -function deleteTableCategory(categoryHeaderRow) { - var categoryName = categoryHeaderRow.data("category"); +function deleteTableCategory($categoryHeaderRow) { + var categoryName = $categoryHeaderRow.data("category"); - categoryHeaderRow + $categoryHeaderRow .closest("table") .find("tr[data-category='" + categoryName + "']") .each(function () { - deleteTableRow($(this)); + if ($(this).hasClass(Settings.DATA_ROW_CLASS)) { + deleteTableRow($(this)); + } else { + $(this).remove(); + } }); } From 987a8c802f06e0443b1a0e0058e1910d10652803 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Thu, 28 Jul 2016 15:41:47 -0700 Subject: [PATCH 51/98] Add basic input validation --- .../resources/web/settings/js/settings.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 4b258fa9fb..e59405981f 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1412,6 +1412,17 @@ function deleteTableRow($row) { function addTableCategory($categoryInputRow) { var $input = $categoryInputRow.find("input").first(); + var categoryValue = $input.prop("value"); + if (!categoryValue || $categoryInputRow.closest("table").find("tr[data-category='" + categoryValue + "']").length !== 0) { + $categoryInputRow.addClass("has-warning"); + + setTimeout(function () { + $categoryInputRow.removeClass("has-warning"); + }, 1000); + + return; + } + var $rowInput = $categoryInputRow.next(".inputs").clone(); if (!$rowInput) { console.error("Error cloning inputs"); @@ -1420,7 +1431,6 @@ function addTableCategory($categoryInputRow) { var canAddRows = $categoryInputRow.data("can-add-rows"); var message = $categoryInputRow.data("message"); var categoryKey = $categoryInputRow.data("key"); - var categoryValue = $input.prop("value"); var width = 0; $categoryInputRow .children("td") @@ -1438,8 +1448,6 @@ function addTableCategory($categoryInputRow) { .attr("data-category", categoryValue) .addClass(Settings.NEW_ROW_CLASS); - // TODO: create inputs on initial template load - var $newCategoryRow = $(makeTableCategoryHeader(categoryKey, categoryValue, width, true, " - " + message)); $newCategoryRow.addClass(Settings.NEW_ROW_CLASS); From 91f847a7f0aa8561a63119bb612d6b9c4e2e34b7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 28 Jul 2016 17:24:35 -0700 Subject: [PATCH 52/98] Update progress bar image --- scripts/system/assets/images/progress-bar.svg | 9 +++++++-- scripts/system/progress.js | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/system/assets/images/progress-bar.svg b/scripts/system/assets/images/progress-bar.svg index bd35b4ce53..360aa3f1a9 100644 --- a/scripts/system/assets/images/progress-bar.svg +++ b/scripts/system/assets/images/progress-bar.svg @@ -1,7 +1,12 @@ - - + + + + + + + diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 660dfc4b98..fc813ada88 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -250,7 +250,7 @@ displayProgress += diff * 0.1; } } - print('PROGRESS:', displayProgress); + //print('PROGRESS:', displayProgress); // Update state From bcff561eb89d124e0b5bc9a0a354a325f300c8ce Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Thu, 28 Jul 2016 17:14:48 -0700 Subject: [PATCH 53/98] Add category collapsing and expanding --- domain-server/resources/web/css/style.css | 17 ++++++-- .../resources/web/settings/js/settings.js | 40 ++++++++++++++++--- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index 0a3dc3dab5..b66b7df258 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -37,8 +37,9 @@ body { margin-right: auto; } -.table .value-category { +.value-category:not(.inputs) { font-weight: bold; + background: #f5f5f5; } .table .value-category [message]::after { @@ -47,8 +48,18 @@ body { font-weight: normal; } -.table .value-category.inputs { - font-weight: normal; +.table .value-row.contracted, +.table .inputs.contracted { + display: none; +} + +.toggle-category { + cursor: pointer; +} + +.toggle-category-icon { + padding: 4px; + margin-right: 8px; } .glyphicon-remove { diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index e59405981f..06c6520c26 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -14,6 +14,11 @@ var Settings = { DEL_ROW_SPAN_CLASSES: 'glyphicon glyphicon-remove del-row', ADD_CATEGORY_BUTTON_CLASS: 'add-category', ADD_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-plus add-category', + TOGGLE_CATEGORY_COLUMN_CLASS: 'toggle-category', + TOGGLE_CATEGORY_SPAN_CLASS: 'toggle-category-icon', + TOGGLE_CATEGORY_SPAN_CLASSES: 'glyphicon toggle-category-icon', + TOGGLE_CATEGORY_EXPANDED_CLASS: 'glyphicon-triangle-bottom', + TOGGLE_CATEGORY_CONTRACTED_CLASS: 'glyphicon-triangle-right', DEL_CATEGORY_BUTTON_CLASS: 'del-category', DEL_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-remove del-category', MOVE_UP_BUTTON_CLASS: 'move-up', @@ -184,6 +189,10 @@ $(document).ready(function(){ deleteTableCategory($(this).closest('tr')); }); + $('#' + Settings.FORM_ID).on('click', '.' + Settings.TOGGLE_CATEGORY_COLUMN_CLASS, function(){ + toggleTableCategory($(this).closest('tr')); + }); + $('#' + Settings.FORM_ID).on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){ moveTableRow($(this).closest('tr'), true); }); @@ -937,9 +946,10 @@ $('body').on('click', '.save-button', function(e){ }); function makeTable(setting, keypath, setting_value, isLocked) { - // TODO: enforce category sorting var isArray = !_.has(setting, 'key'); - + var categoryKey = setting.categorize_by_key; + var isCategorized = !!categoryKey && isArray; + if (!isArray && setting.can_order) { setting.can_order = false; } @@ -1013,8 +1023,6 @@ function makeTable(setting, keypath, setting_value, isLocked) { var rowIsObject = setting.columns.length > 1; _.each(setting_value, function(row, rowIndexOrName) { - var categoryKey = setting.categorize_by_key; - var isCategorized = !!categoryKey && isArray; var categoryPair = {}; var categoryValue = ""; if (isCategorized) { @@ -1118,7 +1126,8 @@ function makeTable(setting, keypath, setting_value, isLocked) { function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, canRemove, message) { var html = "" + - "" + + "" + + "" + "" + categoryValue + "" + "" + ((canRemove) ? ( @@ -1418,7 +1427,7 @@ function addTableCategory($categoryInputRow) { setTimeout(function () { $categoryInputRow.removeClass("has-warning"); - }, 1000); + }, 400); return; } @@ -1477,6 +1486,25 @@ function deleteTableCategory($categoryHeaderRow) { }); } +function toggleTableCategory($categoryHeaderRow) { + var $icon = $categoryHeaderRow.find("." + Settings.TOGGLE_CATEGORY_SPAN_CLASS).first(); + var categoryName = $categoryHeaderRow.data("category"); + var wasExpanded = $icon.hasClass(Settings.TOGGLE_CATEGORY_EXPANDED_CLASS); + if (wasExpanded) { + $icon + .addClass(Settings.TOGGLE_CATEGORY_CONTRACTED_CLASS) + .removeClass(Settings.TOGGLE_CATEGORY_EXPANDED_CLASS); + } else { + $icon + .addClass(Settings.TOGGLE_CATEGORY_EXPANDED_CLASS) + .removeClass(Settings.TOGGLE_CATEGORY_CONTRACTED_CLASS); + } + $categoryHeaderRow + .closest("table") + .find("tr[data-category='" + categoryName + "']") + .toggleClass("contracted", wasExpanded); +} + function moveTableRow(row, move_up) { var table = $(row).closest('table') var isArray = table.data('setting-type') === 'array' From 6279b96223aaa24d90f994b39f001e669c0c9b22 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 29 Jul 2016 10:11:12 -0700 Subject: [PATCH 54/98] Add minor tweaks to progress.js --- scripts/system/progress.js | 60 ++++++++++---------------------------- 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/scripts/system/progress.js b/scripts/system/progress.js index fc813ada88..0628974043 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -28,10 +28,10 @@ FADE_OUT_WAIT = 1000, // Wait before starting to fade out after progress 100%. visible = false, BAR_WIDTH = 480, // Dimension of SVG in pixels of visible portion (half) of the bar. - BAR_HEIGHT = 30, + BAR_HEIGHT = 10, BAR_URL = Script.resolvePath("assets/images/progress-bar.svg"), - BACKGROUND_WIDTH = 540, - BACKGROUND_HEIGHT = 90, + BACKGROUND_WIDTH = 520, + BACKGROUND_HEIGHT = 50, BACKGROUND_URL = Script.resolvePath("assets/images/progress-bar-background.svg"), isOnHMD = false, windowWidth = 0, @@ -45,8 +45,9 @@ PROGRESS_3D_DIRECTION = 0.0, // Degrees from avatar orientation. PROGRESS_3D_DISTANCE = 0.602, // Horizontal distance from avatar position. PROGRESS_3D_ELEVATION = -0.8, // Height of top middle of top notification relative to avatar eyes. + PROGRESS_3D_ELEVATION = -0.0, // Height of top middle of top notification relative to avatar eyes. PROGRESS_3D_YAW = 0.0, // Degrees relative to notifications direction. - PROGRESS_3D_PITCH = -60.0, // Degrees from vertical. + PROGRESS_3D_PITCH = -0.0, // Degrees from vertical. SCALE_3D = 0.0011, // Scale the bar SVG for 3D display. BACKGROUND_3D_SIZE = { x: 0.76, @@ -57,7 +58,7 @@ green: 2, blue: 2 }, - BACKGROUND_3D_ALPHA = 0.7; + BACKGROUND_3D_ALPHA = 1.0; function fade() { @@ -119,6 +120,7 @@ wasActive = false; rawProgress = 100; bestRawProgress = 100; + cooldown = 0; } else { var count = info.downloading.length + info.pending; if (!wasActive) { @@ -128,17 +130,8 @@ if (count > maxSeen) { maxSeen = count; } - if (cooldown < 0) { - print("PROGRESS: OUT OF COOLDOWN"); - //for (i = 0; i < info.downloading.length; i += 1) { - //rawProgress += info.downloading[i]; - //} - //rawProgress = rawProgress / (info.downloading.length + info.pending); + if (cooldown <= 0) { rawProgress = ((maxSeen - count) / maxSeen) * 100; - //rawProgress += 1; - //if (rawProgress > 90) { - //rawProgress = 20 - //} if (rawProgress > bestRawProgress) { bestRawProgress = rawProgress; @@ -208,46 +201,23 @@ var b = 0; function update() { cooldown -= 16; - /* - maxSeen = 100; - b++; - rawProgress = b; - if (rawProgress == 100) { - b = 0 - } - bestRawProgress = rawProgress; - print(rawProgress, bestRawProgress); - */ - - //print(rawProgress, bestRawProgress, maxSeen); var viewport, eyePosition, avatarOrientation; hmdActive = HMD.active; - //if (isOnHMD !== HMD.active) { - //deleteOverlays(); - //isOnHMD = !isOnHMD; - //createOverlays(); - //} + // if (isOnHMD !== HMD.active) { + // deleteOverlays(); + // isOnHMD = !isOnHMD; + // createOverlays(); + // } - /* - // Calculate progress value to display - if (rawProgress === 0 && displayProgress <= DISPLAY_PROGRESS_MINOR_MAXIMUM) { - displayProgress = Math.min(displayProgress + DISPLAY_PROGRESS_MINOR_INCREMENT, DISPLAY_PROGRESS_MINOR_MAXIMUM); - } else if (rawProgress < displayProgress) { - displayProgress = rawProgress; - } else if (rawProgress > displayProgress) { - displayProgress = Math.min(rawProgress, displayProgress + DISPLAY_PROGRESS_MAJOR_INCREMENT); - } // else (rawProgress === displayProgress); do nothing. - //displayProgress = bestRawProgress; - //*/ if (displayProgress < rawProgress) { var diff = rawProgress - displayProgress; if (diff < 0.1) { displayProgress = rawProgress; } else { - displayProgress += diff * 0.1; + displayProgress += diff * 0.05; } } //print('PROGRESS:', displayProgress); @@ -323,7 +293,7 @@ windowWidth = viewport.x; windowHeight = viewport.y; - var yOffset = hmdActive ? -300 : 0; + var yOffset = hmdActive ? -300 : -10; Overlays.editOverlay(background2D.overlay, { x: windowWidth / 2 - background2D.width / 2, From 9f6da23b15b7d9d14ce24dd7353383c34a38665e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 29 Jul 2016 10:11:28 -0700 Subject: [PATCH 55/98] Update progress bar images --- scripts/system/assets/images/progress-bar-background.svg | 4 ++-- scripts/system/assets/images/progress-bar.svg | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/system/assets/images/progress-bar-background.svg b/scripts/system/assets/images/progress-bar-background.svg index 732acd05ad..0499edfa16 100644 --- a/scripts/system/assets/images/progress-bar-background.svg +++ b/scripts/system/assets/images/progress-bar-background.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/assets/images/progress-bar.svg b/scripts/system/assets/images/progress-bar.svg index 360aa3f1a9..e24a2cbff4 100644 --- a/scripts/system/assets/images/progress-bar.svg +++ b/scripts/system/assets/images/progress-bar.svg @@ -1,12 +1,12 @@ + viewBox="0 0 960 10" enable-background="new -159 536 960 30" xml:space="preserve"> - - + + From 39c8ce2a1af0d933f2978629c8b4057d06ee7424 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 29 Jul 2016 10:19:18 -0700 Subject: [PATCH 56/98] Fix new group message to be more intuitive --- domain-server/resources/describe-settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index f4ceb0f432..7600aefc09 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -459,7 +459,7 @@ "can_add_new_categories": true, "can_add_new_rows": false, "new_category_placeholder": "Add Group", - "new_category_message": "Reload to see ranks", + "new_category_message": "Save and reload to see ranks", "groups": [ { @@ -554,7 +554,7 @@ "can_add_new_categories": true, "can_add_new_rows": false, "new_category_placeholder": "Add Blacklist Group", - "new_category_message": "Reload to see ranks", + "new_category_message": "Save and reload to see ranks", "groups": [ { From 8e1820f911cb42e5f75ae4191d632ddc8f7983c2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 29 Jul 2016 13:10:14 -0700 Subject: [PATCH 57/98] make sure permissions have the correct ID in the case where a user has been verified --- domain-server/src/DomainGatekeeper.cpp | 1 + libraries/networking/src/NodePermissions.h | 1 + 2 files changed, 2 insertions(+) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 946e855d0d..60ba409d3b 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -137,6 +137,7 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin qDebug() << "| user-permissions: unverified or no username for" << userPerms.getID() << ", so:" << userPerms; #endif } else { + userPerms.setID(verifiedUsername); if (_server->_settingsManager.havePermissionsForName(verifiedUsername)) { userPerms = _server->_settingsManager.getPermissionsForName(verifiedUsername); userPerms.setVerifiedUserName(verifiedUsername); diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 07b47037b8..6f4309a447 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -34,6 +34,7 @@ public: NodePermissions(QMap perms); const QString& getID() const { return _id; } // a user-name or a group-name, not verified + void setID(const QString& id) { _id = id; } void setRankID(QUuid& rankID) { _rankID = rankID; } const QUuid& getRankID() const { return _rankID; } NodePermissionsKey getKey() const { return NodePermissionsKey(_id, _rankID); } From 800c2b9ec0fcdb1f038b8713b4db59abcb4e84b2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 30 Jul 2016 11:21:40 +1200 Subject: [PATCH 58/98] Refresh file dialog when directory content changes --- interface/resources/qml/dialogs/FileDialog.qml | 11 +++++++++++ libraries/ui/src/FileDialogHelper.cpp | 12 ++++++++++++ libraries/ui/src/FileDialogHelper.h | 12 +++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index fa5be18cd3..6a37886cb3 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -87,6 +87,15 @@ ModalWindow { currentSelection.text = d.capitalizeDrive(helper.urlToPath(initialFolder)); } + helper.contentsChanged.connect(function() { + if (folderListModel) { + // Make folderListModel refresh. + var save = folderListModel.folder; + folderListModel.folder = ""; + folderListModel.folder = save; + } + }); + fileTableView.forceActiveFocus(); } @@ -343,12 +352,14 @@ ModalWindow { onFolderChanged: { if (folder === rootFolder) { model = driveListModel; + helper.monitorDirectory(""); update(); } else { var needsUpdate = model === driveListModel && folder === folderListModel.folder; model = folderListModel; folderListModel.folder = folder; + helper.monitorDirectory(helper.urlToPath(folder)); if (needsUpdate) { update(); diff --git a/libraries/ui/src/FileDialogHelper.cpp b/libraries/ui/src/FileDialogHelper.cpp index 3a12e054df..9d791ec562 100644 --- a/libraries/ui/src/FileDialogHelper.cpp +++ b/libraries/ui/src/FileDialogHelper.cpp @@ -115,3 +115,15 @@ QList FileDialogHelper::urlToList(const QUrl& url) { return results; } +void FileDialogHelper::monitorDirectory(const QString& path) { + if (!_fsWatcherPath.isEmpty()) { + _fsWatcher.removePath(_fsWatcherPath); + _fsWatcherPath = ""; + } + + if (!path.isEmpty()) { + _fsWatcher.addPath(path); + _fsWatcherPath = path; + connect(&_fsWatcher, &QFileSystemWatcher::directoryChanged, this, &FileDialogHelper::contentsChanged); + } +} diff --git a/libraries/ui/src/FileDialogHelper.h b/libraries/ui/src/FileDialogHelper.h index 6058f8f7bb..6c352ecdfc 100644 --- a/libraries/ui/src/FileDialogHelper.h +++ b/libraries/ui/src/FileDialogHelper.h @@ -9,11 +9,12 @@ #ifndef hifi_ui_FileDialogHelper_h #define hifi_ui_FileDialogHelper_h +#include #include #include -#include #include #include +#include class FileDialogHelper : public QObject { @@ -61,6 +62,15 @@ public: Q_INVOKABLE QList urlToList(const QUrl& url); Q_INVOKABLE void openDirectory(const QString& path); + + Q_INVOKABLE void monitorDirectory(const QString& path); + +signals: + void contentsChanged(); + +private: + QFileSystemWatcher _fsWatcher; + QString _fsWatcherPath; }; From ebdfede89fe7b3944709f0c4cf0a495cafc9a37a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 29 Jul 2016 16:34:59 -0700 Subject: [PATCH 59/98] switch to new friends api --- domain-server/src/DomainGatekeeper.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 60ba409d3b..f3a7febb9d 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -826,20 +826,33 @@ void DomainGatekeeper::getDomainOwnerFriendsList() { callbackParams.errorCallbackReceiver = this; callbackParams.errorCallbackMethod = "getDomainOwnerFriendsListErrorCallback"; - const QString GET_FRIENDS_LIST_PATH = "api/v1/users"; - QUrlQuery query; - query.addQueryItem("filter", "friends"); - + const QString GET_FRIENDS_LIST_PATH = "api/v1/user/friends"; DependencyManager::get()->sendRequest(GET_FRIENDS_LIST_PATH, AccountManagerAuth::Required, QNetworkAccessManager::GetOperation, callbackParams, QByteArray(), - NULL, QVariantMap(), query); + NULL, QVariantMap()); } void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requestReply) { + // { + // status: "success", + // data: { + // friends: [ + // "chris", + // "freidrica", + // "G", + // "huffman", + // "leo", + // "philip", + // "ryan", + // "sam", + // "ZappoMan" + // ] + // } + // } QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); if (jsonObject["status"].toString() == "success") { _domainOwnerFriends.clear(); - QJsonArray friends = jsonObject["data"].toObject()["users"].toArray(); + QJsonArray friends = jsonObject["data"].toObject()["friends"].toArray(); for (int i = 0; i < friends.size(); i++) { QString friendUserName = friends.at(i).toObject()["username"].toString(); _domainOwnerFriends += friendUserName; From 2443166d78dc5706755ebf1a61fb3b7dd12e14bc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 29 Jul 2016 16:41:17 -0700 Subject: [PATCH 60/98] update for new friends api --- domain-server/src/DomainGatekeeper.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index f3a7febb9d..36f70f5463 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -854,8 +854,7 @@ void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requ _domainOwnerFriends.clear(); QJsonArray friends = jsonObject["data"].toObject()["friends"].toArray(); for (int i = 0; i < friends.size(); i++) { - QString friendUserName = friends.at(i).toObject()["username"].toString(); - _domainOwnerFriends += friendUserName; + _domainOwnerFriends += friends.at(i).toString() } } else { qDebug() << "getDomainOwnerFriendsList api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); From 5d50389e29adaead7dc9428ddab62af704d9d9d6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 29 Jul 2016 17:17:59 -0700 Subject: [PATCH 61/98] Make updates to the hmd progress bar --- .../assets/images/progress-bar-background.svg | 2 +- scripts/system/progress.js | 242 +++++++++++------- 2 files changed, 152 insertions(+), 92 deletions(-) diff --git a/scripts/system/assets/images/progress-bar-background.svg b/scripts/system/assets/images/progress-bar-background.svg index 0499edfa16..a8b4e1aab5 100644 --- a/scripts/system/assets/images/progress-bar-background.svg +++ b/scripts/system/assets/images/progress-bar-background.svg @@ -1,5 +1,5 @@  - + diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 0628974043..faf8d1bd38 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -33,7 +33,7 @@ BACKGROUND_WIDTH = 520, BACKGROUND_HEIGHT = 50, BACKGROUND_URL = Script.resolvePath("assets/images/progress-bar-background.svg"), - isOnHMD = false, + use3DOverlay = false, windowWidth = 0, windowHeight = 0, background2D = {}, @@ -45,10 +45,10 @@ PROGRESS_3D_DIRECTION = 0.0, // Degrees from avatar orientation. PROGRESS_3D_DISTANCE = 0.602, // Horizontal distance from avatar position. PROGRESS_3D_ELEVATION = -0.8, // Height of top middle of top notification relative to avatar eyes. - PROGRESS_3D_ELEVATION = -0.0, // Height of top middle of top notification relative to avatar eyes. + PROGRESS_3D_ELEVATION = -0.2, // Height of top middle of top notification relative to avatar eyes. PROGRESS_3D_YAW = 0.0, // Degrees relative to notifications direction. PROGRESS_3D_PITCH = -0.0, // Degrees from vertical. - SCALE_3D = 0.0011, // Scale the bar SVG for 3D display. + SCALE_3D = 0.0004, // Scale the bar SVG for 3D display. BACKGROUND_3D_SIZE = { x: 0.76, y: 0.08 @@ -66,9 +66,7 @@ if (alpha < 0) { alpha = 0; - } - - if (alpha > 1) { + } else if (alpha > 1) { alpha = 1; } @@ -81,9 +79,9 @@ visible = false; } - if (isOnHMD) { + if (use3DOverlay) { Overlays.editOverlay(background3D.overlay, { - backgroundAlpha: alpha * BACKGROUND_3D_ALPHA, + alpha: alpha, visible: visible }); } else { @@ -92,7 +90,7 @@ visible: visible }); } - Overlays.editOverlay(isOnHMD ? bar3D.overlay : bar2D.overlay, { + Overlays.editOverlay(use3DOverlay ? bar3D.overlay : bar2D.overlay, { alpha: alpha, visible: visible }); @@ -105,7 +103,12 @@ displayProgress = 0; } - Window.domainChanged.connect(resetProgress); + Window.domainChanged.connect(function() { + hasShownOnThisDomain = false; + resetProgress(); + }); + + var hasShownOnThisDomain = false; var maxSeen = 0; var bestRawProgress = 0; @@ -114,7 +117,7 @@ function onDownloadInfoChanged(info) { var i; - print("PROGRESS: DOwnload info changed ", info.downloading.length, info.pending, maxSeen); + print("PROGRESS: Download info changed ", info.downloading.length, info.pending, maxSeen); // Update raw progress value if (info.downloading.length + info.pending === 0) { wasActive = false; @@ -142,79 +145,87 @@ } function createOverlays() { - if (isOnHMD) { + background3D.overlay = Overlays.addOverlay("image3d", { + url: BACKGROUND_URL, + subImage: { + x: 0, + y: 0, + width: BACKGROUND_WIDTH, + height: BACKGROUND_HEIGHT + }, + scale: SCALE_3D * BACKGROUND_WIDTH, + isFacingAvatar: false, + visible: false, + alpha: 1.0, + ignoreRayIntersection: true, + emissive: true, + isFacingAvatar: true, + drawInFront: true + }); + bar3D.overlay = Overlays.addOverlay("image3d", { + url: BAR_URL, + subImage: { + x: 0, + y: 0, + width: BAR_WIDTH, + height: BAR_HEIGHT + }, + scale: SCALE_3D * BAR_WIDTH, + isFacingAvatar: false, + visible: false, + alpha: 1.0, + ignoreRayIntersection: true, + emissive: true, + isFacingAvatar: true, + drawInFront: true + }); - background3D.overlay = Overlays.addOverlay("rectangle3d", { - size: BACKGROUND_3D_SIZE, - color: BACKGROUND_3D_COLOR, - alpha: BACKGROUND_3D_ALPHA, - solid: true, - isFacingAvatar: false, - visible: false, - ignoreRayIntersection: true - }); - bar3D.overlay = Overlays.addOverlay("image3d", { - url: BAR_URL, - subImage: { - x: 0, - y: 0, - width: 480, - height: BAR_HEIGHT - }, - scale: SCALE_3D * BAR_WIDTH, - isFacingAvatar: false, - visible: false, - alpha: 0.0, - ignoreRayIntersection: true - }); - - } else { - - background2D.overlay = Overlays.addOverlay("image", { - imageURL: BACKGROUND_URL, - width: background2D.width, - height: background2D.height, - visible: false, - alpha: 0.0 - }); - bar2D.overlay = Overlays.addOverlay("image", { - imageURL: BAR_URL, - subImage: { - x: 0, - y: 0, - width: BAR_WIDTH, - height: BAR_HEIGHT - }, - width: bar2D.width, - height: bar2D.height, - visible: false, - alpha: 0.0 - }); - } + background2D.overlay = Overlays.addOverlay("image", { + imageURL: BACKGROUND_URL, + width: background2D.width, + height: background2D.height, + visible: false, + alpha: 0.0 + }); + bar2D.overlay = Overlays.addOverlay("image", { + imageURL: BAR_URL, + subImage: { + x: 0, + y: 0, + width: BAR_WIDTH, + height: BAR_HEIGHT + }, + width: bar2D.width, + height: bar2D.height, + visible: false, + alpha: 0.0 + }); } function deleteOverlays() { - Overlays.deleteOverlay(isOnHMD ? background3D.overlay : background2D.overlay); - Overlays.deleteOverlay(isOnHMD ? bar3D.overlay : bar2D.overlay); + Overlays.deleteOverlay(background3D.overlay); + Overlays.deleteOverlay(bar3D.overlay); + + Overlays.deleteOverlay(background2D.overlay); + Overlays.deleteOverlay(bar2D.overlay); } var b = 0; + var worldOverlayOn = false; + var currentOrientation = null; function update() { - cooldown -= 16; + cooldown -= 30; var viewport, eyePosition, avatarOrientation; - hmdActive = HMD.active; - // if (isOnHMD !== HMD.active) { - // deleteOverlays(); - // isOnHMD = !isOnHMD; - // createOverlays(); - // } + if (use3DOverlay !== worldOverlayOn) { + use3DOverlay = !use3DOverlay; + } if (displayProgress < rawProgress) { var diff = rawProgress - displayProgress; - if (diff < 0.1) { + if (diff < 0.5) { displayProgress = rawProgress; } else { displayProgress += diff * 0.05; @@ -222,7 +233,6 @@ } //print('PROGRESS:', displayProgress); - // Update state if (!visible) { // Not visible because no recent downloads if (displayProgress < 100) { // Have started downloading so fade in @@ -257,11 +267,19 @@ } } + if (use3DOverlay) { + Overlays.editOverlay(background2D.overlay, { visible: false }); + Overlays.editOverlay(bar2D.overlay, { visible: false }); + } else { + Overlays.editOverlay(background3D.overlay, { visible: false }); + Overlays.editOverlay(bar3D.overlay, { visible: false }); + } + if (visible) { // Update progress bar - Overlays.editOverlay(isOnHMD ? bar3D.overlay : bar2D.overlay, { - visible: visible, + Overlays.editOverlay(use3DOverlay ? bar3D.overlay : bar2D.overlay, { + visible: true, subImage: { x: BAR_WIDTH * (1 - displayProgress / 100), y: 0, @@ -270,19 +288,27 @@ }, }); + Overlays.editOverlay(use3DOverlay ? background3D.overlay : background2D.overlay, { + visible: true, + }); + // Update position - if (isOnHMD) { + if (use3DOverlay) { + print("HERE"); // Update 3D overlays to maintain positions relative to avatar eyePosition = MyAvatar.getDefaultEyePosition(); - avatarOrientation = MyAvatar.orientation; + avatarOrientation = Camera.orientation; + + currentOrientation = Quat.slerp(currentOrientation, avatarOrientation, 0.10); + avatarOrientation = currentOrientation; Overlays.editOverlay(background3D.overlay, { position: Vec3.sum(eyePosition, Vec3.multiplyQbyV(avatarOrientation, background3D.offset)), - rotation: Quat.multiply(avatarOrientation, background3D.orientation) + //rotation: Quat.multiply(avatarOrientation, background3D.orientation) }); Overlays.editOverlay(bar3D.overlay, { position: Vec3.sum(eyePosition, Vec3.multiplyQbyV(avatarOrientation, bar3D.offset)), - rotation: Quat.multiply(avatarOrientation, bar3D.orientation) + //rotation: Quat.multiply(avatarOrientation, bar3D.orientation) }); } else { @@ -290,25 +316,44 @@ viewport = Controller.getViewportDimensions(); if (viewport.x !== windowWidth || viewport.y !== windowHeight) { - windowWidth = viewport.x; - windowHeight = viewport.y; - - var yOffset = hmdActive ? -300 : -10; - - Overlays.editOverlay(background2D.overlay, { - x: windowWidth / 2 - background2D.width / 2, - y: windowHeight - background2D.height - bar2D.height + yOffset - }); - - Overlays.editOverlay(bar2D.overlay, { - x: windowWidth / 2 - bar2D.width / 2, - y: windowHeight - background2D.height - bar2D.height + (background2D.height - bar2D.height) / 2 + yOffset - }); + updateProgressBarLocation(); } } } } + function updateProgressBarLocation() { + viewport = Controller.getViewportDimensions(); + windowWidth = viewport.x; + windowHeight = viewport.y; + + var yOffset = HMD.active ? -300 : -10; + yOffset += yAdjust; + // if (hmdActive) { + // if (true) { //hasShownOnThisDomain) { + // yOffset = -100; + // SCALE_2D = 1.0/3; + // } else { + // yOffset = -300; + // SCALE_2D = 2.0/3; + // } + // } + background2D.width = SCALE_2D * BACKGROUND_WIDTH; + background2D.height = SCALE_2D * BACKGROUND_HEIGHT; + bar2D.width = SCALE_2D * BAR_WIDTH; + bar2D.height = SCALE_2D * BAR_HEIGHT; + + Overlays.editOverlay(background2D.overlay, { + x: windowWidth / 2 - background2D.width / 2, + y: windowHeight - background2D.height - bar2D.height + yOffset + }); + + Overlays.editOverlay(bar2D.overlay, { + x: windowWidth / 2 - bar2D.width / 2, + y: windowHeight - background2D.height - bar2D.height + (background2D.height - bar2D.height) / 2 + yOffset + }); + } + function setProgressBar(progress) { } @@ -339,10 +384,25 @@ deleteOverlays(); } + var yAdjust = 0; + function keyPress(event) { + print("Key event: ", event.text); + if (event.text == '.') { + yAdjust -= 10; + updateProgressBarLocation(); + } else if (event.text == ',') { + yAdjust += 10; + updateProgressBarLocation(); + } else if (event.text == 'SPACE') { + worldOverlayOn = !worldOverlayOn; + } + } + setUp(); GlobalServices.downloadInfoChanged.connect(onDownloadInfoChanged); GlobalServices.updateDownloadInfo(); //Script.update.connect(update); - Script.setInterval(update, 16); + Script.setInterval(update, 1000/60); Script.scriptEnding.connect(tearDown); + Controller.keyPressEvent.connect(keyPress); }()); From c72cfb165c7b72add5c3d622099256d8f14e8c90 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 29 Jul 2016 18:33:06 -0700 Subject: [PATCH 62/98] damnit --- domain-server/src/DomainGatekeeper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 36f70f5463..5b2e5a2bb0 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -854,7 +854,7 @@ void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requ _domainOwnerFriends.clear(); QJsonArray friends = jsonObject["data"].toObject()["friends"].toArray(); for (int i = 0; i < friends.size(); i++) { - _domainOwnerFriends += friends.at(i).toString() + _domainOwnerFriends += friends.at(i).toString(); } } else { qDebug() << "getDomainOwnerFriendsList api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); From 60d66739500b1f057a75d4048d54071dfd158656 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Aug 2016 12:00:08 -0700 Subject: [PATCH 63/98] switch data-web urls to point at production server rather than dev one --- domain-server/resources/web/settings/js/settings.js | 4 +--- domain-server/src/DomainServer.cpp | 4 +--- libraries/networking/src/NetworkingConstants.h | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 06c6520c26..c1005de105 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1,8 +1,6 @@ var Settings = { showAdvanced: false, - // METAVERSE_URL: 'https://metaverse.highfidelity.com', - // METAVERSE_URL: 'http://localhost:3000', - METAVERSE_URL: 'https://devdataweb.highfidelity.com', + METAVERSE_URL: 'https://metaverse.highfidelity.com', ADVANCED_CLASS: 'advanced-setting', TRIGGER_CHANGE_CLASS: 'trigger-change', DATA_ROW_CLASS: 'value-row', diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d972b8f641..8af364992c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -45,9 +45,7 @@ int const DomainServer::EXIT_CODE_REBOOT = 234923; -// const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com"; -// const QString ICE_SERVER_DEFAULT_HOSTNAME = "localhost"; -const QString ICE_SERVER_DEFAULT_HOSTNAME = "devdataweb.highfidelity.com"; +const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com"; DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 62a8f29088..a512ae8887 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -15,9 +15,7 @@ #include namespace NetworkingConstants { - // const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com"); - // const QUrl METAVERSE_SERVER_URL = QUrl("http://localhost:3000"); - const QUrl METAVERSE_SERVER_URL = QUrl("https://devdataweb.highfidelity.com"); + const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com"); } #endif // hifi_NetworkingConstants_h From 31ac7a2812dc6775d7a4596cfb30283a21af6329 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 2 Aug 2016 12:40:01 +1200 Subject: [PATCH 64/98] Add OverlayWebWindow resized, moved, and closed signals --- interface/resources/qml/QmlWebWindow.qml | 17 +++++++++++++++++ .../qml/windows/DefaultFrameDecoration.qml | 5 ++++- interface/resources/qml/windows/Window.qml | 1 + libraries/ui/src/QmlWindowClass.cpp | 9 +++++++++ libraries/ui/src/QmlWindowClass.h | 1 + 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index 542b44b95e..153498e2f7 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -39,6 +39,23 @@ Windows.ScrollingWindow { // missing signal signal sendToScript(var message); + signal moved(vector2d position); + signal resized(size size); + + function notifyMoved() { + moved(Qt.vector2d(x, y)); + } + + function notifyResized() { + resized(Qt.size(width, height)); + } + + onXChanged: notifyMoved(); + onYChanged: notifyMoved(); + + onWidthChanged: notifyResized(); + onHeightChanged: notifyResized(); + Item { width: pane.contentWidth implicitHeight: pane.scrollHeight diff --git a/interface/resources/qml/windows/DefaultFrameDecoration.qml b/interface/resources/qml/windows/DefaultFrameDecoration.qml index ce47b818b1..40e32aaa6b 100644 --- a/interface/resources/qml/windows/DefaultFrameDecoration.qml +++ b/interface/resources/qml/windows/DefaultFrameDecoration.qml @@ -78,7 +78,10 @@ Decoration { id: closeClickArea anchors.fill: parent hoverEnabled: true - onClicked: window.shown = false; + onClicked: { + window.shown = false; + window.windowClosed(); + } } } } diff --git a/interface/resources/qml/windows/Window.qml b/interface/resources/qml/windows/Window.qml index c873872692..40ef74c59b 100644 --- a/interface/resources/qml/windows/Window.qml +++ b/interface/resources/qml/windows/Window.qml @@ -30,6 +30,7 @@ Fadable { // // Signals // + signal windowClosed(); signal windowDestroyed(); signal mouseEntered(); signal mouseExited(); diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 7d56e51495..c3ca5f54d9 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -124,6 +124,10 @@ void QmlWindowClass::initQml(QVariantMap properties) { // Forward messages received from QML on to the script connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SLOT(qmlToScript(const QVariant&)), Qt::QueuedConnection); connect(_qmlWindow, SIGNAL(visibleChanged()), this, SIGNAL(visibleChanged()), Qt::QueuedConnection); + + connect(_qmlWindow, SIGNAL(resized(QSizeF)), this, SIGNAL(resized(QSizeF)), Qt::QueuedConnection); + connect(_qmlWindow, SIGNAL(moved(QVector2D)), this, SLOT(hasMoved(QVector2D)), Qt::QueuedConnection); + connect(_qmlWindow, SIGNAL(windowClosed()), this, SLOT(hasClosed()), Qt::QueuedConnection); }); } Q_ASSERT(_qmlWindow); @@ -259,7 +263,12 @@ void QmlWindowClass::close() { } } +void QmlWindowClass::hasMoved(QVector2D position) { + emit moved(glm::vec2(position.x(), position.y())); +} + void QmlWindowClass::hasClosed() { + emit closed(); } void QmlWindowClass::raise() { diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index c30027df6e..07cf736334 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -62,6 +62,7 @@ signals: void fromQml(const QVariant& message); protected slots: + void hasMoved(QVector2D); void hasClosed(); void qmlToScript(const QVariant& message); From 6836b09f8740d9c43a7e3cb4e2efaf8aaa6a3da8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 15:36:12 -0700 Subject: [PATCH 65/98] add sections to DS security for IP/Subnet --- .../resources/describe-settings.json | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 7600aefc09..6081a603f3 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -638,6 +638,148 @@ } ] }, + { + "name": "ip_permissions", + "type": "table", + "caption": "Permissions for Users from IP Addresses or Subnets", + "can_add_new_rows": true, + "groups": [ + { + "label": "IP/Subnet", + "span": 1 + }, + { + "label": "Permissions ?", + "span": 6 + } + ], + + "columns": [ + { + "name": "permissions_id", + "label": "IP/Subnet" + }, + { + "name": "group_id", + "label": "Group ID", + "readonly": true, + "hidden": true + }, + { + "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": "ip_forbidden", + "type": "table", + "caption": "Permissions denied to Users from IP addresses or Subnets", + "can_add_new_rows": true, + "groups": [ + { + "label": "IP/Subnet", + "span": 1 + }, + { + "label": "Permissions ?", + "span": 6 + } + ], + + "columns": [ + { + "name": "permissions_id", + "label": "IP/Subnet" + }, + { + "name": "group_id", + "label": "Group ID", + "readonly": true, + "hidden": true + }, + { + "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": "permissions", "type": "table", From edce249d2c82fe1a6aff3d41e49ca4e718c04485 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 15:42:33 -0700 Subject: [PATCH 66/98] remove subnet from IP address security --- .../resources/describe-settings.json | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 6081a603f3..1e8c0238cb 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -641,15 +641,15 @@ { "name": "ip_permissions", "type": "table", - "caption": "Permissions for Users from IP Addresses or Subnets", + "caption": "Permissions for Users from IP Addresses", "can_add_new_rows": true, "groups": [ { - "label": "IP/Subnet", + "label": "IP Address", "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], @@ -657,13 +657,7 @@ "columns": [ { "name": "permissions_id", - "label": "IP/Subnet" - }, - { - "name": "group_id", - "label": "Group ID", - "readonly": true, - "hidden": true + "label": "" }, { "name": "id_can_connect", @@ -712,15 +706,15 @@ { "name": "ip_forbidden", "type": "table", - "caption": "Permissions denied to Users from IP addresses or Subnets", + "caption": "Permissions denied to Users from IP Addresses", "can_add_new_rows": true, "groups": [ { - "label": "IP/Subnet", + "label": "IP", "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], @@ -728,13 +722,7 @@ "columns": [ { "name": "permissions_id", - "label": "IP/Subnet" - }, - { - "name": "group_id", - "label": "Group ID", - "readonly": true, - "hidden": true + "label": "" }, { "name": "id_can_connect", From a674c843c28294d0adccad517772c4ad8d5628a2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 16:40:51 -0700 Subject: [PATCH 67/98] add unpack for IP, DRYup unpackPermissions --- .../src/DomainServerSettingsManager.cpp | 154 +++++++----------- .../src/DomainServerSettingsManager.h | 15 ++ 2 files changed, 77 insertions(+), 92 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index a02b19f9fd..7a31aca4e3 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -430,114 +430,84 @@ void DomainServerSettingsManager::packPermissions() { _configMap.loadMasterAndUserConfig(_argumentList); } -void DomainServerSettingsManager::unpackPermissions() { - // transfer details from _configMap to _agentPermissions; +bool DomainServerSettingsManager::unpackPermissionsForKeypath(const QString& keyPath, + NodePermissionsMap* mapPointer, + std::function customUnpacker) { - _standardAgentPermissions.clear(); - _agentPermissions.clear(); - _groupPermissions.clear(); - _groupForbiddens.clear(); + mapPointer->clear(); - bool foundLocalhost = false; - bool foundAnonymous = false; - bool foundLoggedIn = false; - bool foundFriends = false; - bool needPack = false; - - QVariant* standardPermissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_STANDARD_PERMISSIONS_KEYPATH); - if (!standardPermissions || !standardPermissions->canConvert(QMetaType::QVariantList)) { - qDebug() << "failed to extract standard permissions from settings."; - standardPermissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_STANDARD_PERMISSIONS_KEYPATH, true); - (*standardPermissions) = QVariantList(); - } - QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_PERMISSIONS_KEYPATH); - if (!permissions || !permissions->canConvert(QMetaType::QVariantList)) { - qDebug() << "failed to extract permissions from settings."; - permissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_PERMISSIONS_KEYPATH, true); + QVariant* permissions = valueForKeyPath(_configMap.getMergedConfig(), keyPath, true); + if (!permissions->canConvert(QMetaType::QVariantList)) { + qDebug() << "Failed to extract permissions for key path" << keyPath << "from settings."; (*permissions) = QVariantList(); } - QVariant* groupPermissions = valueForKeyPath(_configMap.getUserConfig(), GROUP_PERMISSIONS_KEYPATH); - if (!groupPermissions || !groupPermissions->canConvert(QMetaType::QVariantList)) { - qDebug() << "failed to extract group permissions from settings."; - groupPermissions = valueForKeyPath(_configMap.getUserConfig(), GROUP_PERMISSIONS_KEYPATH, true); - (*groupPermissions) = QVariantList(); - } - QVariant* groupForbiddens = valueForKeyPath(_configMap.getUserConfig(), GROUP_FORBIDDENS_KEYPATH); - if (!groupForbiddens || !groupForbiddens->canConvert(QMetaType::QVariantList)) { - qDebug() << "failed to extract group forbiddens from settings."; - groupForbiddens = valueForKeyPath(_configMap.getUserConfig(), GROUP_FORBIDDENS_KEYPATH, true); - (*groupForbiddens) = QVariantList(); - } - QList standardPermissionsList = standardPermissions->toList(); - foreach (QVariant permsHash, standardPermissionsList) { - NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; - QString id = perms->getID(); - NodePermissionsKey idKey = NodePermissionsKey(id, 0); - foundLocalhost |= (idKey == NodePermissions::standardNameLocalhost); - foundAnonymous |= (idKey == NodePermissions::standardNameAnonymous); - foundLoggedIn |= (idKey == NodePermissions::standardNameLoggedIn); - foundFriends |= (idKey == NodePermissions::standardNameFriends); - if (_standardAgentPermissions.contains(idKey)) { - qDebug() << "duplicate name in standard permissions table: " << id; - *(_standardAgentPermissions[idKey]) |= *perms; - needPack = true; - } else { - _standardAgentPermissions[idKey] = perms; - } - } + bool needPack = false; QList permissionsList = permissions->toList(); foreach (QVariant permsHash, permissionsList) { NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; QString id = perms->getID(); - NodePermissionsKey idKey = NodePermissionsKey(id, 0); - if (_agentPermissions.contains(idKey)) { - qDebug() << "duplicate name in permissions table: " << id; - *(_agentPermissions[idKey]) |= *perms; + + NodePermissionsKey idKey = perms->getKey(); + + if (mapPointer->contains(idKey)) { + qDebug() << "Duplicate name in permissions table for" << keyPath << " - " << id; + (*mapPointer)[idKey] |= perms; needPack = true; } else { - _agentPermissions[idKey] = perms; + (*mapPointer)[idKey] = perms; + } + + if (customUnpacker) { + customUnpacker(perms); } } - QList groupPermissionsList = groupPermissions->toList(); - foreach (QVariant permsHash, groupPermissionsList) { - NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; - QString id = perms->getID(); - NodePermissionsKey idKey = perms->getKey(); - if (_groupPermissions.contains(idKey)) { - qDebug() << "duplicate name in group permissions table: " << id; - *(_groupPermissions[idKey]) |= *perms; - needPack = true; - } else { - *(_groupPermissions[idKey]) = *perms; - } - if (perms->isGroup()) { - // the group-id was cached. hook-up the uuid in the uuid->group hash - _groupPermissionsByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRankID())] = _groupPermissions[idKey]; - needPack |= setGroupID(perms->getID(), perms->getGroupID()); - } - } + return needPack; - QList groupForbiddensList = groupForbiddens->toList(); - foreach (QVariant permsHash, groupForbiddensList) { - NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; - QString id = perms->getID(); - NodePermissionsKey idKey = perms->getKey(); - if (_groupForbiddens.contains(idKey)) { - qDebug() << "duplicate name in group forbiddens table: " << id; - *(_groupForbiddens[idKey]) |= *perms; - needPack = true; - } else { - _groupForbiddens[idKey] = perms; - } - if (perms->isGroup()) { - // the group-id was cached. hook-up the uuid in the uuid->group hash - _groupForbiddensByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRankID())] = _groupForbiddens[idKey]; - needPack |= setGroupID(perms->getID(), perms->getGroupID()); - } - } +} + +void DomainServerSettingsManager::unpackPermissions() { + // transfer details from _configMap to _agentPermissions; + + bool foundLocalhost = false; + bool foundAnonymous = false; + bool foundLoggedIn = false; + bool foundFriends = false; + + bool needPack = false; + + needPack |= unpackPermissionsForKeypath(AGENT_STANDARD_PERMISSIONS_KEYPATH, &_standardAgentPermissions, + [&foundLocalhost, &foundAnonymous, &foundLoggedIn, &foundFriends](NodePermissionsPointer perms){ + NodePermissionsKey idKey = perms->getKey(); + foundLocalhost |= (idKey == NodePermissions::standardNameLocalhost); + foundAnonymous |= (idKey == NodePermissions::standardNameAnonymous); + foundLoggedIn |= (idKey == NodePermissions::standardNameLoggedIn); + foundFriends |= (idKey == NodePermissions::standardNameFriends); + }); + + needPack |= unpackPermissionsForKeypath(AGENT_PERMISSIONS_KEYPATH, &_agentPermissions); + needPack |= unpackPermissionsForKeypath(IP_PERMISSIONS_KEYPATH, &_ipPermissions); + needPack |= unpackPermissionsForKeypath(IP_FORBIDDENS_KEYPATH, &_ipForbiddens); + + needPack |= unpackPermissionsForKeypath(GROUP_PERMISSIONS_KEYPATH, &_groupPermissions, + [&](NodePermissionsPointer perms){ + if (perms->isGroup()) { + // the group-id was cached. hook-up the uuid in the uuid->group hash + _groupPermissionsByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRankID())] = _groupPermissions[perms->getKey()]; + needPack |= setGroupID(perms->getID(), perms->getGroupID()); + } + }); + + needPack |= unpackPermissionsForKeypath(GROUP_FORBIDDENS_KEYPATH, &_groupForbiddens, + [&](NodePermissionsPointer perms) { + if (perms->isGroup()) { + // the group-id was cached. hook-up the uuid in the uuid->group hash + _groupForbiddensByUUID[GroupByUUIDKey(perms->getGroupID(), perms->getRankID())] = _groupForbiddens[perms->getKey()]; + needPack |= setGroupID(perms->getID(), perms->getGroupID()); + } + }); // if any of the standard names are missing, add them if (!foundLocalhost) { diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index f56b1ecd21..754b62a72b 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -27,6 +27,8 @@ const QString SETTINGS_PATH = "/settings"; const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json"; const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permissions"; const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions"; +const QString IP_PERMISSIONS_KEYPATH = "security.ip_permissions"; +const QString IP_FORBIDDENS_KEYPATH = "security.ip_forbiddens"; const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions"; const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens"; @@ -58,6 +60,14 @@ public: NodePermissions getPermissionsForName(const NodePermissionsKey& key) const { return getPermissionsForName(key.first); } QStringList getAllNames() const; + // these give access to permissions for specific IPs from the domain-server settings page + bool havePermissionsForIP(const QHostAddress& address) const { return _ipPermissions.contains(address.toString(), 0); } + NodePermissions getPermissionsForIP(const QHostAddress& address) const; + + // these remove permissions from users connecting from specific IPs + bool haveForbiddensForIP(const QHostAddress& address) const { return _ipForbiddens.contains(address.toString(), 0); } + NodePermissions getForbiddensForIP(const QHostAddress& address) 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); @@ -129,11 +139,16 @@ private: void packPermissionsForMap(QString mapName, NodePermissionsMap& permissionsRows, QString keyPath); void packPermissions(); void unpackPermissions(); + bool unpackPermissionsForKeypath(const QString& keyPath, NodePermissionsMap* destinationMapPointer, + std::function customUnpacker = {}); bool ensurePermissionsForGroupRanks(); NodePermissionsMap _standardAgentPermissions; // anonymous, logged-in, localhost, friend-of-domain-owner NodePermissionsMap _agentPermissions; // specific account-names + NodePermissionsMap _ipPermissions; // permissions granted by node IP address + NodePermissionsMap _ipForbiddens; // permissions denied by node IP address + NodePermissionsMap _groupPermissions; // permissions granted by membership to specific groups NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group // these are like _groupPermissions and _groupForbiddens but with uuids rather than group-names in the keys From f8ea1801277cc612a0fa13698910525eeeebc50a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 16:42:54 -0700 Subject: [PATCH 68/98] match naming of forbidden IP settings group to groups --- domain-server/resources/describe-settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 1e8c0238cb..11d21b3ab0 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -704,7 +704,7 @@ ] }, { - "name": "ip_forbidden", + "name": "ip_forbiddens", "type": "table", "caption": "Permissions denied to Users from IP Addresses", "can_add_new_rows": true, From cd436355fc6a193c97529c3a1180d245a9bf0fe4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 16:50:34 -0700 Subject: [PATCH 69/98] cleanup handling of defaults for standard user perms --- .../src/DomainServerSettingsManager.cpp | 53 +++++++------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 7a31aca4e3..341275fe13 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -469,23 +469,11 @@ bool DomainServerSettingsManager::unpackPermissionsForKeypath(const QString& key } void DomainServerSettingsManager::unpackPermissions() { - // transfer details from _configMap to _agentPermissions; - - bool foundLocalhost = false; - bool foundAnonymous = false; - bool foundLoggedIn = false; - bool foundFriends = false; + // transfer details from _configMap to _agentPermissions bool needPack = false; - needPack |= unpackPermissionsForKeypath(AGENT_STANDARD_PERMISSIONS_KEYPATH, &_standardAgentPermissions, - [&foundLocalhost, &foundAnonymous, &foundLoggedIn, &foundFriends](NodePermissionsPointer perms){ - NodePermissionsKey idKey = perms->getKey(); - foundLocalhost |= (idKey == NodePermissions::standardNameLocalhost); - foundAnonymous |= (idKey == NodePermissions::standardNameAnonymous); - foundLoggedIn |= (idKey == NodePermissions::standardNameLoggedIn); - foundFriends |= (idKey == NodePermissions::standardNameFriends); - }); + needPack |= unpackPermissionsForKeypath(AGENT_STANDARD_PERMISSIONS_KEYPATH, &_standardAgentPermissions); needPack |= unpackPermissionsForKeypath(AGENT_PERMISSIONS_KEYPATH, &_agentPermissions); needPack |= unpackPermissionsForKeypath(IP_PERMISSIONS_KEYPATH, &_ipPermissions); @@ -510,26 +498,23 @@ void DomainServerSettingsManager::unpackPermissions() { }); // if any of the standard names are missing, add them - if (!foundLocalhost) { - NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLocalhost) }; - perms->setAll(true); - _standardAgentPermissions[perms->getKey()] = perms; - needPack = true; - } - if (!foundAnonymous) { - NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameAnonymous) }; - _standardAgentPermissions[perms->getKey()] = perms; - needPack = true; - } - if (!foundLoggedIn) { - NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLoggedIn) }; - _standardAgentPermissions[perms->getKey()] = perms; - needPack = true; - } - if (!foundFriends) { - NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameFriends) }; - _standardAgentPermissions[perms->getKey()] = perms; - needPack = true; + foreach(const QString& standardName, NodePermissions::standardNames) { + NodePermissionsKey standardKey { standardName, 0 }; + if (!_standardAgentPermissions.contains(standardKey)) { + // we don't have permissions for one of the standard groups, so we'll add them now + NodePermissionsPointer perms { new NodePermissions(standardKey) }; + + // the localhost user is granted all permissions by default + if (standardKey == NodePermissions::standardNameLocalhost) { + perms->setAll(true); + } + + // add the permissions to the standard map + _standardAgentPermissions[standardKey] = perms; + + // this will require a packing of permissions + needPack = true; + } } needPack |= ensurePermissionsForGroupRanks(); From f7298ef767e64fbd5154152beb8fe09cc92ba0e0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 17:12:16 -0700 Subject: [PATCH 70/98] add packing of IP perms, small cleanup in packing --- .../src/DomainServerSettingsManager.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 341275fe13..d5bc732c13 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -354,16 +354,14 @@ void DomainServerSettingsManager::packPermissionsForMap(QString mapName, NodePermissionsMap& permissionsRows, QString keyPath) { // find (or create) the "security" section of the settings map - QVariant* security = valueForKeyPath(_configMap.getUserConfig(), "security"); - if (!security || !security->canConvert(QMetaType::QVariantMap)) { - security = valueForKeyPath(_configMap.getUserConfig(), "security", true); + QVariant* security = valueForKeyPath(_configMap.getUserConfig(), "security", true); + if (!security->canConvert(QMetaType::QVariantMap)) { (*security) = QVariantMap(); } // find (or create) whichever subsection of "security" we are packing - QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath); - if (!permissions || !permissions->canConvert(QMetaType::QVariantList)) { - permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath, true); + QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath, true); + if (!permissions->canConvert(QMetaType::QVariantList)) { (*permissions) = QVariantList(); } @@ -420,6 +418,12 @@ void DomainServerSettingsManager::packPermissions() { // save settings for specific users packPermissionsForMap("permissions", _agentPermissions, AGENT_PERMISSIONS_KEYPATH); + // save settings for IP addresses + packPermissionsForMap("permissions", _ipPermissions, IP_PERMISSIONS_KEYPATH); + + // save settings for IP address blacklist + packPermissionsForMap("permissions", _ipForbiddens, IP_FORBIDDENS_KEYPATH); + // save settings for groups packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH); From b1e0e6d7081ccc356ef04d5274af91387e4003bf Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 17:35:22 -0700 Subject: [PATCH 71/98] use replacement for IP address perms, not additive --- .../resources/describe-settings.json | 67 +------------------ domain-server/src/DomainGatekeeper.cpp | 33 +++++++-- domain-server/src/DomainGatekeeper.h | 3 +- .../src/DomainServerSettingsManager.cpp | 17 +++-- .../src/DomainServerSettingsManager.h | 7 +- 5 files changed, 44 insertions(+), 83 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 11d21b3ab0..cfba2ae4f3 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -649,72 +649,7 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 6 - } - ], - - "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": "ip_forbiddens", - "type": "table", - "caption": "Permissions denied to Users from IP Addresses", - "can_add_new_rows": true, - "groups": [ - { - "label": "IP", - "span": 1 - }, - { - "label": "Permissions ?", + "label": "Permissions ?", "span": 6 } ], diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 5b2e5a2bb0..0abbf38c72 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -120,8 +120,9 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer_settingsManager.hasPermissionsForIP(senderAddress)) { + // this user comes from an IP we have in our permissions table, apply those permissions + userPerms = _server->_settingsManager.getPermissionsForIP(senderAddress); + +#ifdef WANT_DEBUG + qDebug() << "| user-permissions: specific IP matches, so:" << userPerms; +#endif + } } else { userPerms.setID(verifiedUsername); if (_server->_settingsManager.havePermissionsForName(verifiedUsername)) { userPerms = _server->_settingsManager.getPermissionsForName(verifiedUsername); - userPerms.setVerifiedUserName(verifiedUsername); #ifdef WANT_DEBUG qDebug() << "| user-permissions: specific user 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 + userPerms = _server->_settingsManager.getPermissionsForIP(senderAddress); + +#ifdef WANT_DEBUG + qDebug() << "| user-permissions: specific IP matches, so:" << userPerms; #endif } else { - userPerms.setVerifiedUserName(verifiedUsername); + // they are logged into metaverse, but we don't have specific permissions for them. userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn); #ifdef WANT_DEBUG @@ -191,6 +207,8 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin } } } + + userPerms.setVerifiedUserName(verifiedUsername); } #ifdef WANT_DEBUG @@ -225,7 +243,12 @@ void DomainGatekeeper::updateNodePermissions() { const QHostAddress& addr = node->getLocalSocket().getAddress(); bool isLocalUser = (addr == limitedNodeList->getLocalSockAddr().getAddress() || addr == QHostAddress::LocalHost); - userPerms = setPermissionsForUser(isLocalUser, verifiedUsername); + + // at this point we don't have a sending socket for packets from this node - assume it is the active socket + // or the public socket if we haven't activated a socket for the node yet + HifiSockAddr connectingAddr = node->getActiveSocket() ? node->getPublicSocket() : *node->getActiveSocket(); + + userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress()); } node->setPermissions(userPerms); @@ -337,7 +360,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect } } - userPerms = setPermissionsForUser(isLocalUser, verifiedUsername); + userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, nodeConnection.senderSockAddr.getAddress()); if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) { sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.", diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 12697b8f3b..06ecfcf285 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -106,7 +106,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); + NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress); + void getGroupMemberships(const QString& username); // void getIsGroupMember(const QString& username, const QUuid groupID); void getDomainOwnerFriendsList(); diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index d5bc732c13..81794b2899 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -421,9 +421,6 @@ void DomainServerSettingsManager::packPermissions() { // save settings for IP addresses packPermissionsForMap("permissions", _ipPermissions, IP_PERMISSIONS_KEYPATH); - // save settings for IP address blacklist - packPermissionsForMap("permissions", _ipForbiddens, IP_FORBIDDENS_KEYPATH); - // save settings for groups packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH); @@ -457,7 +454,7 @@ bool DomainServerSettingsManager::unpackPermissionsForKeypath(const QString& key if (mapPointer->contains(idKey)) { qDebug() << "Duplicate name in permissions table for" << keyPath << " - " << id; - (*mapPointer)[idKey] |= perms; + *((*mapPointer)[idKey]) |= *perms; needPack = true; } else { (*mapPointer)[idKey] = perms; @@ -480,8 +477,8 @@ void DomainServerSettingsManager::unpackPermissions() { needPack |= unpackPermissionsForKeypath(AGENT_STANDARD_PERMISSIONS_KEYPATH, &_standardAgentPermissions); needPack |= unpackPermissionsForKeypath(AGENT_PERMISSIONS_KEYPATH, &_agentPermissions); + needPack |= unpackPermissionsForKeypath(IP_PERMISSIONS_KEYPATH, &_ipPermissions); - needPack |= unpackPermissionsForKeypath(IP_FORBIDDENS_KEYPATH, &_ipForbiddens); needPack |= unpackPermissionsForKeypath(GROUP_PERMISSIONS_KEYPATH, &_groupPermissions, [&](NodePermissionsPointer perms){ @@ -634,6 +631,16 @@ NodePermissions DomainServerSettingsManager::getPermissionsForName(const QString return nullPermissions; } +NodePermissions DomainServerSettingsManager::getPermissionsForIP(const QHostAddress& address) const { + NodePermissionsKey ipKey = NodePermissionsKey(address.toString(), 0); + if (_ipPermissions.contains(ipKey)) { + return *(_ipPermissions[ipKey].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 754b62a72b..440cd8fe24 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -28,7 +28,6 @@ const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json"; const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permissions"; const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions"; const QString IP_PERMISSIONS_KEYPATH = "security.ip_permissions"; -const QString IP_FORBIDDENS_KEYPATH = "security.ip_forbiddens"; const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions"; const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens"; @@ -61,13 +60,9 @@ public: QStringList getAllNames() const; // these give access to permissions for specific IPs from the domain-server settings page - bool havePermissionsForIP(const QHostAddress& address) const { return _ipPermissions.contains(address.toString(), 0); } + bool hasPermissionsForIP(const QHostAddress& address) const { return _ipPermissions.contains(address.toString(), 0); } NodePermissions getPermissionsForIP(const QHostAddress& address) const; - // these remove permissions from users connecting from specific IPs - bool haveForbiddensForIP(const QHostAddress& address) const { return _ipForbiddens.contains(address.toString(), 0); } - NodePermissions getForbiddensForIP(const QHostAddress& address) 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); From 5d69c9c81ed8e89651d39288665ae2857c1b3b76 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 17:54:57 -0700 Subject: [PATCH 72/98] fix sockaddr choice for local nodes --- domain-server/src/DomainGatekeeper.cpp | 2 +- domain-server/src/DomainServerSettingsManager.cpp | 2 +- domain-server/src/DomainServerSettingsManager.h | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 0abbf38c72..eb907aace4 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -246,7 +246,7 @@ void DomainGatekeeper::updateNodePermissions() { // at this point we don't have a sending socket for packets from this node - assume it is the active socket // or the public socket if we haven't activated a socket for the node yet - HifiSockAddr connectingAddr = node->getActiveSocket() ? node->getPublicSocket() : *node->getActiveSocket(); + HifiSockAddr connectingAddr = node->getActiveSocket() ? *node->getActiveSocket() : node->getPublicSocket(); userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress()); } diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 81794b2899..72aa5bada6 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -528,7 +528,7 @@ void DomainServerSettingsManager::unpackPermissions() { qDebug() << "--------------- permissions ---------------------"; QList> permissionsSets; permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get() - << _groupPermissions.get() << _groupForbiddens.get(); + << _groupPermissions.get() << _groupForbiddens.get() << _ipPermissions.get(); foreach (auto permissionSet, permissionsSets) { QHashIterator i(permissionSet); while (i.hasNext()) { diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 440cd8fe24..49c3ac95b7 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -142,7 +142,6 @@ private: NodePermissionsMap _agentPermissions; // specific account-names NodePermissionsMap _ipPermissions; // permissions granted by node IP address - NodePermissionsMap _ipForbiddens; // permissions denied by node IP address NodePermissionsMap _groupPermissions; // permissions granted by membership to specific groups NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group From f04996a53d09ab6690ae07a183c01bf2642edafc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 18:01:04 -0700 Subject: [PATCH 73/98] add the kick permission to domain server settings --- .../resources/describe-settings.json | 45 ++++++++++++++++--- libraries/networking/src/NodePermissions.cpp | 5 +++ libraries/networking/src/NodePermissions.h | 3 +- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index cfba2ae4f3..b2a749a66d 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -395,7 +395,7 @@ }, { "label": "Permissions ?", - "span": 6 + "span": 7 } ], @@ -445,6 +445,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false } ], @@ -468,7 +475,7 @@ }, { "label": "Permissions ?", - "span": 6 + "span": 7 } ], @@ -543,6 +550,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -563,7 +577,7 @@ }, { "label": "Permissions ?", - "span": 6 + "span": 7 } ], @@ -635,6 +649,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -650,7 +671,7 @@ }, { "label": "Permissions ?", - "span": 6 + "span": 7 } ], @@ -700,6 +721,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -716,7 +744,7 @@ }, { "label": "Permissions ?", - "span": 6 + "span": 7 } ], @@ -766,6 +794,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false } ] } diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index a815884dc5..a1d4fc182e 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -44,6 +44,7 @@ NodePermissions::NodePermissions(QMap perms) { permissions |= perms["id_can_write_to_asset_server"].toBool() ? Permission::canWriteToAssetServer : Permission::none; permissions |= perms["id_can_connect_past_max_capacity"].toBool() ? Permission::canConnectPastMaxCapacity : Permission::none; + permissions |= perms["id_can_kick"].toBool() ? Permission::canKick : Permission::none; } QVariant NodePermissions::toVariant(QHash groupRanks) { @@ -63,6 +64,7 @@ QVariant NodePermissions::toVariant(QHash groupRanks) { values["id_can_rez_tmp"] = can(Permission::canRezTemporaryEntities); values["id_can_write_to_asset_server"] = can(Permission::canWriteToAssetServer); values["id_can_connect_past_max_capacity"] = can(Permission::canConnectPastMaxCapacity); + values["id_can_kick"] = can(Permission::canKick); return QVariant(values); } @@ -123,6 +125,9 @@ QDebug operator<<(QDebug debug, const NodePermissions& perms) { if (perms.can(NodePermissions::Permission::canConnectPastMaxCapacity)) { debug << " ignore-max-cap"; } + if (perms.can(NodePermissions::Permission::canKick)) { + debug << " kick"; + } debug.nospace() << "]"; return debug.nospace(); } diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 6f4309a447..5d2755f9b5 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -63,7 +63,8 @@ public: canRezPermanentEntities = 4, canRezTemporaryEntities = 8, canWriteToAssetServer = 16, - canConnectPastMaxCapacity = 32 + canConnectPastMaxCapacity = 32, + canKick = 64 }; Q_DECLARE_FLAGS(Permissions, Permission) Permissions permissions; From eaf52ae11118dbed0b6e5181df465e668e35477f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 18:23:21 -0700 Subject: [PATCH 74/98] default kick perms for localhost --- domain-server/resources/describe-settings.json | 2 +- domain-server/src/DomainServerSettingsManager.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index b2a749a66d..524a687772 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1,5 +1,5 @@ { - "version": 1.5, + "version": 1.6, "settings": [ { "name": "metaverse", diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 72aa5bada6..aba2c25db5 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -273,6 +273,15 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList // This was prior to operating hours, so add default hours validateDescriptorsMap(); } + + if (oldVersion < 1.6) { + unpackPermissions(); + + // This was prior to addition of kick permissions, add that to localhost permissions by default + _standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canKick); + + packPermissions(); + } } unpackPermissions(); From 94b97e8119598cb32de3538f6b3564a5ed4d6b70 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 18:23:37 -0700 Subject: [PATCH 75/98] expose kick perms from LNL and Node --- libraries/networking/src/LimitedNodeList.h | 1 + libraries/networking/src/Node.h | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 7cbdcd5361..2b2f660569 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -110,6 +110,7 @@ public: bool getThisNodeCanRez() const { return _permissions.can(NodePermissions::Permission::canRezPermanentEntities); } bool getThisNodeCanRezTmp() const { return _permissions.can(NodePermissions::Permission::canRezTemporaryEntities); } bool getThisNodeCanWriteAssets() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); } + bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); } quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); } QUdpSocket& getDTLSSocket(); diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 270814c35c..18088c6cea 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -69,6 +69,7 @@ public: bool getCanRez() const { return _permissions.can(NodePermissions::Permission::canRezPermanentEntities); } bool getCanRezTmp() const { return _permissions.can(NodePermissions::Permission::canRezTemporaryEntities); } bool getCanWriteToAssetServer() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); } + bool getCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); } void parseIgnoreRequestMessage(QSharedPointer message); void addIgnoredNode(const QUuid& otherNodeID); From 7e6dbf2d1a8869c2d28619ddd08b13fd182cb155 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jul 2016 18:39:22 -0700 Subject: [PATCH 76/98] add a kick request packet and send from NL --- libraries/networking/src/NodeList.cpp | 27 ++++++++++++++++++- libraries/networking/src/NodeList.h | 2 ++ .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 ++- .../src/UsersScriptingInterface.cpp | 5 ++++ .../src/UsersScriptingInterface.h | 1 + 6 files changed, 37 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 05b5532560..781cc00c1c 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -727,7 +727,7 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID) { emit ignoredNode(nodeID); } else { - qWarning() << "UsersScriptingInterface::ignore called with an invalid ID or an ID which matches the current session ID."; + qWarning() << "NodeList::ignoreNodeBySessionID called with an invalid ID or an ID which matches the current session ID."; } } @@ -759,3 +759,28 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { } } } + +void NodeList::kickNodeBySessionID(const QUuid& nodeID) { + // send a request to domain-server to kick the node with the given session ID + // the domain-server will handle the persistence of the kick (via username or IP) + + if (!nodeID.isNull() && _sessionUUID != nodeID ) { + if (getThisNodeCanKick()) { + // setup the packet + auto kickPacket = NLPacket::create(PacketType::NodeKickRequest, NUM_BYTES_RFC4122_UUID, true); + + // write the node ID to the packet + kickPacket->write(nodeID.toRfc4122()); + + qDebug() << "Sending packet to kick node" << uuidStringWithoutCurlyBraces(nodeID); + + sendPacket(std::move(kickPacket), _domainHandler.getSockAddr()); + } else { + qWarning() << "You do not have permissions to kick in this domain." + << "Request to kick node" << uuidStringWithoutCurlyBraces(nodeID) << "will not be sent"; + } + } else { + qWarning() << "NodeList::kickNodeBySessionID called with an invalid ID or an ID which matches the current session ID."; + + } +} diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index ff994ce612..f3cd5bed0d 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -73,6 +73,8 @@ public: void ignoreNodeBySessionID(const QUuid& nodeID); bool isIgnoringNode(const QUuid& nodeID) const; + void kickNodeBySessionID(const QUuid& nodeID); + public slots: void reset(); void sendDomainServerCheckIn(); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index ce1f25d45d..e9d61a827a 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -26,7 +26,7 @@ const QSet NON_VERIFIED_PACKETS = QSet() << PacketType::NodeJsonStats << PacketType::EntityQuery << PacketType::OctreeDataNack << PacketType::EntityEditNack << PacketType::DomainListRequest << PacketType::StopNode - << PacketType::DomainDisconnectRequest; + << PacketType::DomainDisconnectRequest << PacketType::NodeKickRequest; const QSet NON_SOURCED_PACKETS = QSet() << PacketType::StunResponse << PacketType::CreateAssignment << PacketType::RequestAssignment diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 7281e24fa9..40524e2288 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -98,7 +98,8 @@ public: NegotiateAudioFormat, SelectedAudioFormat, MoreEntityShapes, - LAST_PACKET_TYPE = MoreEntityShapes + NodeKickRequest, + LAST_PACKET_TYPE = NodeKickRequest }; }; diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index ff7ccb0164..6be3aa39ac 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -17,3 +17,8 @@ void UsersScriptingInterface::ignore(const QUuid& nodeID) { // ask the NodeList to ignore this user (based on the session ID of their node) DependencyManager::get()->ignoreNodeBySessionID(nodeID); } + +void UsersScriptingInterface::kick(const QUuid& nodeID) { + // ask the NodeList to kick the user with the given session ID + DependencyManager::get()->kickNodeBySessionID(nodeID); +} diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 0dc62c088c..c5929dc8b2 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -22,6 +22,7 @@ class UsersScriptingInterface : public QObject, public Dependency { public slots: void ignore(const QUuid& nodeID); + void kick(const QUuid& nodeID); }; From 5b04ab6d530fa8d7347be6841e3548ca0dc7d436 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jul 2016 09:58:34 -0700 Subject: [PATCH 77/98] add node kick request handling to DS --- domain-server/src/DomainServer.cpp | 1 + .../src/DomainServerSettingsManager.cpp | 59 +++++++++++++++++++ .../src/DomainServerSettingsManager.h | 1 + 3 files changed, 61 insertions(+) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 8af364992c..23e37efaf1 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -411,6 +411,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"); // register the gatekeeper for the packets it needs to receive packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket"); diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index aba2c25db5..639098fdef 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -613,6 +613,65 @@ bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() { return changed; } + +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 + if (sendingNode->getCanKick()) { + // pull the UUID being kicked from the packet + QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + if (!nodeUUID.isNull() && nodeUUID != sendingNode->getUUID()) { + // make sure we actually have a node with this UUID + auto limitedNodeList = DependencyManager::get(); + + auto matchingNode = limitedNodeList->nodeWithUUID(nodeUUID); + + if (matchingNode) { + // we have a matching node, time to decide how to store updated permissions for this node + + NodePermissionsPointer destinationPermissions; + + auto verifiedUsername = matchingNode->getPermissions().getVerifiedUserName(); + + if (!verifiedUsername.isEmpty()) { + // if we have a verified user name for this user, we apply the kick to the username + + // grab or create permissions for the given username + destinationPermissions = _agentPermissions[matchingNode->getPermissions().getKey()]; + } else { + // otherwise we apply the kick to the IP from active socket for this node + // (falling back to the public socket if not yet active) + auto& kickAddress = matchingNode->getActiveSocket() + ? matchingNode->getActiveSocket()->getAddress() + : matchingNode->getPublicSocket().getAddress(); + + // grab or create permissions for the given IP address + NodePermissionsKey ipAddressKey(kickAddress.toString(), QUuid()); + destinationPermissions = _ipPermissions[ipAddressKey]; + } + + // ensure that the connect permission is clear + destinationPermissions->clear(NodePermissions::Permission::canConnectToDomain); + + // we've changed permissions, time to store them to disk and emit our signal to say they have changed + packPermissions(); + + emit updateNodePermissions(); + + } else { + qWarning() << "Node kick request received for unknown node. Refusing to process."; + } + } else { + // this isn't a UUID we can use + qWarning() << "Node kick request received for invalid node ID or from node being kicked. Refusing to process."; + } + + } else { + qWarning() << "Refusing to process a kick packet from node" << uuidStringWithoutCurlyBraces(sendingNode->getUUID()) + << "that does not have kick permissions."; + } +} + QStringList DomainServerSettingsManager::getAllNames() const { QStringList result; foreach (auto key, _agentPermissions.keys()) { diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 49c3ac95b7..d551132006 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -105,6 +105,7 @@ public slots: private slots: void processSettingsRequestPacket(QSharedPointer message); + void processNodeKickRequestPacket(QSharedPointer message, SharedNodePointer sendingNode); private: QStringList _argumentList; From 285478f1b25cc920dc3be25004aae7bc0bef4db6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jul 2016 10:23:09 -0700 Subject: [PATCH 78/98] don't repack the same permissions after kick --- .../src/DomainServerSettingsManager.cpp | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 639098fdef..e85eebd309 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -633,9 +633,14 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointergetPermissions().getVerifiedUserName(); + bool hadExistingPermissions = false; + if (!verifiedUsername.isEmpty()) { // if we have a verified user name for this user, we apply the kick to the username + // check if there were already permissions + hadExistingPermissions = havePermissionsForName(verifiedUsername); + // grab or create permissions for the given username destinationPermissions = _agentPermissions[matchingNode->getPermissions().getKey()]; } else { @@ -645,18 +650,37 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointergetActiveSocket()->getAddress() : matchingNode->getPublicSocket().getAddress(); - // grab or create permissions for the given IP address NodePermissionsKey ipAddressKey(kickAddress.toString(), QUuid()); + + // check if there were already permissions for the IP + hadExistingPermissions = hasPermissionsForIP(kickAddress); + + // grab or create permissions for the given IP address destinationPermissions = _ipPermissions[ipAddressKey]; } - // ensure that the connect permission is clear - destinationPermissions->clear(NodePermissions::Permission::canConnectToDomain); + // make sure we didn't already have existing permissions that disallowed connect + if (!hadExistingPermissions + || destinationPermissions->can(NodePermissions::Permission::canConnectToDomain)) { - // we've changed permissions, time to store them to disk and emit our signal to say they have changed - packPermissions(); + qDebug() << "Removing connect permission for node" << uuidStringWithoutCurlyBraces(matchingNode->getUUID()) + << "after kick request"; - emit updateNodePermissions(); + // ensure that the connect permission is clear + destinationPermissions->clear(NodePermissions::Permission::canConnectToDomain); + + // we've changed permissions, time to store them to disk and emit our signal to say they have changed + packPermissions(); + + emit updateNodePermissions(); + } else { + qWarning() << "Received kick request for node" << uuidStringWithoutCurlyBraces(matchingNode->getUUID()) + << "that already did not have permission to connect"; + + // in this case, though we don't expect the node to be connected to the domain, it is + // emit updateNodePermissions so that the DomainGatekeeper kicks it out + emit updateNodePermissions(); + } } else { qWarning() << "Node kick request received for unknown node. Refusing to process."; From 62af203d08f4ffd398e555fc82c0fe176024fcca Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jul 2016 13:19:10 -0700 Subject: [PATCH 79/98] migrate the deprecated merged master/user config --- .../resources/describe-settings.json | 2 +- domain-server/resources/web/css/style.css | 9 ---- .../resources/web/settings/index.shtml | 6 +-- .../resources/web/settings/js/settings.js | 50 +++++++------------ .../src/DomainServerSettingsManager.cpp | 32 ++++++++---- libraries/shared/src/HifiConfigVariantMap.cpp | 13 +++-- libraries/shared/src/HifiConfigVariantMap.h | 1 + 7 files changed, 52 insertions(+), 61 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 524a687772..c888fa301b 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1,5 +1,5 @@ { - "version": 1.6, + "version": 1.7, "settings": [ { "name": "metaverse", diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index b66b7df258..ef967a47bf 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -75,15 +75,6 @@ span.port { color: #666666; } -.locked { - color: #428bca; -} - -.locked-table { - cursor: not-allowed; - background-color: #eee; -} - .advanced-setting { display: none; } diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 3f969ef913..4c937d6139 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -57,15 +57,13 @@
<% _.each(split_settings[0], function(setting) { %> <% keypath = isGrouped ? group.name + "." + setting.name : setting.name %> - <%= getFormGroup(keypath, setting, values, false, - (_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %> + <%= getFormGroup(keypath, setting, values, false) %> <% }); %> <% if (!_.isEmpty(split_settings[1])) { %> <% $("#advanced-toggle-button").show() %> <% _.each(split_settings[1], function(setting) { %> <% keypath = isGrouped ? group.name + "." + setting.name : setting.name %> - <%= getFormGroup(keypath, setting, values, true, - (_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %> + <%= getFormGroup(keypath, setting, values, true) %> <% }); %> <% }%>
diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index c1005de105..42fcb05d2e 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -41,7 +41,7 @@ var Settings = { }; var viewHelpers = { - getFormGroup: function(keypath, setting, values, isAdvanced, isLocked) { + getFormGroup: function(keypath, setting, values, isAdvanced) { form_group = "
"; setting_value = _(values).valueForKeyPath(keypath); @@ -54,9 +54,6 @@ var viewHelpers = { } label_class = 'control-label'; - if (isLocked) { - label_class += ' locked'; - } function common_attrs(extra_classes) { extra_classes = (!_.isUndefined(extra_classes) ? extra_classes : ""); @@ -71,9 +68,8 @@ var viewHelpers = { form_group += "" } - form_group += "
" - form_group += "" + form_group += "
" + form_group += "" if (setting.help) { form_group += "" + setting.help + ""; @@ -88,7 +84,7 @@ var viewHelpers = { } if (input_type === 'table') { - form_group += makeTable(setting, keypath, setting_value, isLocked) + form_group += makeTable(setting, keypath, setting_value) } else { if (input_type === 'select') { form_group += "" + "' value='" + setting_value + "'/>" } form_group += "" + setting.help + "" @@ -459,10 +453,8 @@ function setupHFAccountButton() { $("[data-keypath='metaverse.automatic_networking']").hide(); } - var tokenLocked = _(Settings.data).valueForKeyPath("locked.metaverse.access_token"); - // use the existing getFormGroup helper to ask for a button - var buttonGroup = viewHelpers.getFormGroup('', buttonSetting, Settings.data.values, false, tokenLocked); + var buttonGroup = viewHelpers.getFormGroup('', buttonSetting, Settings.data.values, false); // add the button group to the top of the metaverse panel $('#metaverse .panel-body').prepend(buttonGroup); @@ -673,7 +665,7 @@ function setupPlacesTable() { } // get a table for the places - var placesTableGroup = viewHelpers.getFormGroup('', placesTableSetting, Settings.data.values, false, false); + var placesTableGroup = viewHelpers.getFormGroup('', placesTableSetting, Settings.data.values, false); // append the places table in the right place $('#places_paths .panel-body').prepend(placesTableGroup); @@ -873,10 +865,8 @@ function reloadSettings(callback) { Settings.data = data; Settings.initialValues = form2js('settings-form', ".", false, cleanupFormValues, true); - if (!_.has(data["locked"], "metaverse") && !_.has(data["locked"]["metaverse"], "id")) { - // append the domain selection modal, as long as it's not locked - appendDomainIDButtons(); - } + // append the domain selection modal + appendDomainIDButtons(); // call our method to setup the HF account button setupHFAccountButton(); @@ -889,12 +879,6 @@ function reloadSettings(callback) { $('[data-toggle="tooltip"]').tooltip(); - // add tooltip to locked settings - $('label.locked').tooltip({ - placement: 'right', - title: 'This setting is in the master config file and cannot be changed' - }); - // call the callback now that settings are loaded callback(true); }).fail(function() { @@ -943,11 +927,11 @@ $('body').on('click', '.save-button', function(e){ return false; }); -function makeTable(setting, keypath, setting_value, isLocked) { +function makeTable(setting, keypath, setting_value) { var isArray = !_.has(setting, 'key'); var categoryKey = setting.categorize_by_key; var isCategorized = !!categoryKey && isArray; - + if (!isArray && setting.can_order) { setting.can_order = false; } @@ -961,7 +945,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { var nonDeletableRowKey = setting["non-deletable-row-key"]; var nonDeletableRowValues = setting["non-deletable-row-values"]; - html += ""; @@ -976,7 +960,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { _.each(setting.groups, function (group) { html += "" }) - if (!isLocked && !setting.read_only) { + if (!setting.read_only) { if (setting.can_order) { html += ""; @@ -1004,7 +988,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { (col.class ? col.class : '') + "'>" + col.label + "" // Data }) - if (!isLocked && !setting.read_only) { + if (!setting.read_only) { if (setting.can_order) { numVisibleColumns++; html += "
" + group.label + "" @@ -1108,7 +1092,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { } // populate inputs in the table for new values - if (!isLocked && !setting.read_only) { + if (!setting.read_only) { if (setting.can_add_new_categories) { html += makeTableCategoryInput(setting, numVisibleColumns); } diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index e85eebd309..bd916a43fe 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -95,6 +95,8 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointerclear(); - QVariant* permissions = valueForKeyPath(_configMap.getMergedConfig(), keyPath, true); + QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath, true); if (!permissions->canConvert(QMetaType::QVariantList)) { qDebug() << "Failed to extract permissions for key path" << keyPath << "from settings."; (*permissions) = QVariantList(); @@ -777,7 +788,7 @@ NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid& } QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { - const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath); + const QVariant* foundValue = valueForKeyPath(_configMap.getUserConfig(), keyPath); if (foundValue) { return *foundValue; @@ -860,12 +871,10 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection // setup a JSON Object with descriptions and non-omitted settings const QString SETTINGS_RESPONSE_DESCRIPTION_KEY = "descriptions"; const QString SETTINGS_RESPONSE_VALUE_KEY = "values"; - const QString SETTINGS_RESPONSE_LOCKED_VALUES_KEY = "locked"; QJsonObject rootObject; rootObject[SETTINGS_RESPONSE_DESCRIPTION_KEY] = _descriptionArray; rootObject[SETTINGS_RESPONSE_VALUE_KEY] = responseObjectForType("", true); - rootObject[SETTINGS_RESPONSE_LOCKED_VALUES_KEY] = QJsonDocument::fromVariant(_configMap.getMasterConfig()).object(); connection->respond(HTTPConnection::StatusCode200, QJsonDocument(rootObject).toJson(), "application/json"); } @@ -910,13 +919,13 @@ QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& ty QVariant variantValue; if (!groupKey.isEmpty()) { - QVariant settingsMapGroupValue = _configMap.getMergedConfig().value(groupKey); + QVariant settingsMapGroupValue = _configMap.getUserConfig().value(groupKey); if (!settingsMapGroupValue.isNull()) { variantValue = settingsMapGroupValue.toMap().value(settingName); } } else { - variantValue = _configMap.getMergedConfig().value(settingName); + variantValue = _configMap.getUserConfig().value(settingName); } QJsonValue result; @@ -1208,6 +1217,9 @@ void DomainServerSettingsManager::persistToFile() { settingsFile.write(QJsonDocument::fromVariant(_configMap.getUserConfig()).toJson()); } else { qCritical("Could not write to JSON settings file. Unable to persist settings."); + + // failed to write, reload whatever the current config state is + _configMap.loadConfig(_argumentList); } } diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index 5ae5ff740d..30eb200770 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -111,6 +111,13 @@ void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentLi loadMapFromJSONFile(_masterConfig, masterConfigFilepath); } + // load the user config - that method replaces loadMasterAndUserConfig after the 1.7 migration + loadConfig(argumentList); + + mergeMasterAndUserConfigs(); +} + +void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { // load the user config const QString USER_CONFIG_FILE_OPTION = "--user-config"; static const QString USER_CONFIG_FILE_NAME = "config.json"; @@ -159,12 +166,10 @@ void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentLi } } } - + } - + loadMapFromJSONFile(_userConfig, _userConfigFilename); - - mergeMasterAndUserConfigs(); } void HifiConfigVariantMap::mergeMasterAndUserConfigs() { diff --git a/libraries/shared/src/HifiConfigVariantMap.h b/libraries/shared/src/HifiConfigVariantMap.h index e92561cff5..06e28cf182 100644 --- a/libraries/shared/src/HifiConfigVariantMap.h +++ b/libraries/shared/src/HifiConfigVariantMap.h @@ -21,6 +21,7 @@ public: HifiConfigVariantMap(); void loadMasterAndUserConfig(const QStringList& argumentList); + void loadConfig(const QStringList& argumentList); const QVariantMap& getMasterConfig() const { return _masterConfig; } QVariantMap& getUserConfig() { return _userConfig; } From 39e6ac09746b4d55122309cc26b0361ebbe24964 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jul 2016 13:46:28 -0700 Subject: [PATCH 80/98] cleanup API for HifiConfigVariantMap after migration --- .../src/DomainServerSettingsManager.cpp | 47 +++++++++---------- .../src/DomainServerSettingsManager.h | 3 +- libraries/shared/src/HifiConfigVariantMap.cpp | 2 +- libraries/shared/src/HifiConfigVariantMap.h | 11 +++-- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index bd916a43fe..c47c786efa 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -120,7 +120,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList // This was prior to the introduction of security.restricted_access // If the user has a list of allowed users then set their value for security.restricted_access to true - QVariant* allowedUsers = valueForKeyPath(_configMap.getMergedConfig(), ALLOWED_USERS_SETTINGS_KEYPATH); + QVariant* allowedUsers = _configMap.valueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH); if (allowedUsers && allowedUsers->canConvert(QMetaType::QVariantList) @@ -131,9 +131,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList // In the pre-toggle system the user had a list of allowed users, so // we need to set security.restricted_access to true - QVariant* restrictedAccess = valueForKeyPath(_configMap.getUserConfig(), - RESTRICTED_ACCESS_SETTINGS_KEYPATH, - true); + QVariant* restrictedAccess = _configMap.valueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH, true); *restrictedAccess = QVariant(true); @@ -151,21 +149,20 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList static const QString ENTITY_FILE_PATH_KEYPATH = ENTITY_SERVER_SETTINGS_KEY + ".persistFilePath"; // this was prior to change of poorly named entitiesFileName to entitiesFilePath - QVariant* persistFileNameVariant = valueForKeyPath(_configMap.getMergedConfig(), - ENTITY_SERVER_SETTINGS_KEY + "." + ENTITY_FILE_NAME_KEY); + QVariant* persistFileNameVariant = _configMap.valueForKeyPath(ENTITY_SERVER_SETTINGS_KEY + "." + ENTITY_FILE_NAME_KEY); if (persistFileNameVariant && persistFileNameVariant->canConvert(QMetaType::QString)) { QString persistFileName = persistFileNameVariant->toString(); qDebug() << "Migrating persistFilename to persistFilePath for entity-server settings"; // grab the persistFilePath option, create it if it doesn't exist - QVariant* persistFilePath = valueForKeyPath(_configMap.getUserConfig(), ENTITY_FILE_PATH_KEYPATH, true); + QVariant* persistFilePath = _configMap.valueForKeyPath(ENTITY_FILE_PATH_KEYPATH, true); // write the migrated value *persistFilePath = persistFileName; // remove the old setting - QVariant* entityServerVariant = valueForKeyPath(_configMap.getUserConfig(), ENTITY_SERVER_SETTINGS_KEY); + QVariant* entityServerVariant = _configMap.valueForKeyPath(ENTITY_SERVER_SETTINGS_KEY); if (entityServerVariant && entityServerVariant->canConvert(QMetaType::QVariantMap)) { QVariantMap entityServerMap = entityServerVariant->toMap(); entityServerMap.remove(ENTITY_FILE_NAME_KEY); @@ -187,7 +184,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList // If we have a password in the previous settings file, make it base 64 static const QString BASIC_AUTH_PASSWORD_KEY_PATH { "security.http_password" }; - QVariant* passwordVariant = valueForKeyPath(_configMap.getUserConfig(), BASIC_AUTH_PASSWORD_KEY_PATH); + QVariant* passwordVariant = _configMap.valueForKeyPath(BASIC_AUTH_PASSWORD_KEY_PATH); if (passwordVariant && passwordVariant->canConvert(QMetaType::QString)) { QString plaintextPassword = passwordVariant->toString(); @@ -292,7 +289,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList qDebug() << "Migrating merged config to user config file. The master config file is deprecated."; // replace the user config by the merged config - _configMap.getUserConfig() = _configMap.getMergedConfig(); + _configMap.getConfig() = _configMap.getMergedConfig(); // persist the new config so the user config file has the correctly merged config persistToFile(); @@ -317,9 +314,9 @@ void DomainServerSettingsManager::validateDescriptorsMap() { static const QString WEEKEND_HOURS{ "descriptors.weekend_hours" }; static const QString UTC_OFFSET{ "descriptors.utc_offset" }; - QVariant* weekdayHours = valueForKeyPath(_configMap.getUserConfig(), WEEKDAY_HOURS, true); - QVariant* weekendHours = valueForKeyPath(_configMap.getUserConfig(), WEEKEND_HOURS, true); - QVariant* utcOffset = valueForKeyPath(_configMap.getUserConfig(), UTC_OFFSET, true); + QVariant* weekdayHours = _configMap.valueForKeyPath(WEEKDAY_HOURS, true); + QVariant* weekendHours = _configMap.valueForKeyPath(WEEKEND_HOURS, true); + QVariant* utcOffset = _configMap.valueForKeyPath(UTC_OFFSET, true); static const QString OPEN{ "open" }; static const QString CLOSE{ "close" }; @@ -375,13 +372,13 @@ void DomainServerSettingsManager::packPermissionsForMap(QString mapName, NodePermissionsMap& permissionsRows, QString keyPath) { // find (or create) the "security" section of the settings map - QVariant* security = valueForKeyPath(_configMap.getUserConfig(), "security", true); + QVariant* security = _configMap.valueForKeyPath("security", true); if (!security->canConvert(QMetaType::QVariantMap)) { (*security) = QVariantMap(); } // find (or create) whichever subsection of "security" we are packing - QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath, true); + QVariant* permissions = _configMap.valueForKeyPath(keyPath, true); if (!permissions->canConvert(QMetaType::QVariantList)) { (*permissions) = QVariantList(); } @@ -457,7 +454,7 @@ bool DomainServerSettingsManager::unpackPermissionsForKeypath(const QString& key mapPointer->clear(); - QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath, true); + QVariant* permissions = _configMap.valueForKeyPath(keyPath, true); if (!permissions->canConvert(QMetaType::QVariantList)) { qDebug() << "Failed to extract permissions for key path" << keyPath << "from settings."; (*permissions) = QVariantList(); @@ -788,7 +785,7 @@ NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid& } QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { - const QVariant* foundValue = valueForKeyPath(_configMap.getUserConfig(), keyPath); + const QVariant* foundValue = _configMap.valueForKeyPath(keyPath); if (foundValue) { return *foundValue; @@ -919,13 +916,13 @@ QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& ty QVariant variantValue; if (!groupKey.isEmpty()) { - QVariant settingsMapGroupValue = _configMap.getUserConfig().value(groupKey); + QVariant settingsMapGroupValue = _configMap.value(groupKey); if (!settingsMapGroupValue.isNull()) { variantValue = settingsMapGroupValue.toMap().value(settingName); } } else { - variantValue = _configMap.getUserConfig().value(settingName); + variantValue = _configMap.value(settingName); } QJsonValue result; @@ -1067,7 +1064,7 @@ QJsonObject DomainServerSettingsManager::settingDescriptionFromGroup(const QJson } bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject) { - auto& settingsVariant = _configMap.getUserConfig(); + auto& settingsVariant = _configMap.getConfig(); bool needRestart = false; // Iterate on the setting groups @@ -1179,22 +1176,22 @@ bool permissionVariantLessThan(const QVariant &v1, const QVariant &v2) { void DomainServerSettingsManager::sortPermissions() { // sort the permission-names - QVariant* standardPermissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_STANDARD_PERMISSIONS_KEYPATH); + QVariant* standardPermissions = _configMap.valueForKeyPath(AGENT_STANDARD_PERMISSIONS_KEYPATH); if (standardPermissions && standardPermissions->canConvert(QMetaType::QVariantList)) { QList* standardPermissionsList = reinterpret_cast(standardPermissions); std::sort((*standardPermissionsList).begin(), (*standardPermissionsList).end(), permissionVariantLessThan); } - QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_PERMISSIONS_KEYPATH); + QVariant* permissions = _configMap.valueForKeyPath(AGENT_PERMISSIONS_KEYPATH); if (permissions && permissions->canConvert(QMetaType::QVariantList)) { QList* permissionsList = reinterpret_cast(permissions); std::sort((*permissionsList).begin(), (*permissionsList).end(), permissionVariantLessThan); } - QVariant* groupPermissions = valueForKeyPath(_configMap.getUserConfig(), GROUP_PERMISSIONS_KEYPATH); + QVariant* groupPermissions = _configMap.valueForKeyPath(GROUP_PERMISSIONS_KEYPATH); if (groupPermissions && groupPermissions->canConvert(QMetaType::QVariantList)) { QList* permissionsList = reinterpret_cast(groupPermissions); std::sort((*permissionsList).begin(), (*permissionsList).end(), permissionVariantLessThan); } - QVariant* forbiddenPermissions = valueForKeyPath(_configMap.getUserConfig(), GROUP_FORBIDDENS_KEYPATH); + QVariant* forbiddenPermissions = _configMap.valueForKeyPath(GROUP_FORBIDDENS_KEYPATH); if (forbiddenPermissions && forbiddenPermissions->canConvert(QMetaType::QVariantList)) { QList* permissionsList = reinterpret_cast(forbiddenPermissions); std::sort((*permissionsList).begin(), (*permissionsList).end(), permissionVariantLessThan); @@ -1214,7 +1211,7 @@ void DomainServerSettingsManager::persistToFile() { QFile settingsFile(_configMap.getUserConfigFilename()); if (settingsFile.open(QIODevice::WriteOnly)) { - settingsFile.write(QJsonDocument::fromVariant(_configMap.getUserConfig()).toJson()); + settingsFile.write(QJsonDocument::fromVariant(_configMap.getConfig()).toJson()); } else { qCritical("Could not write to JSON settings file. Unable to persist settings."); diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index d551132006..144589326c 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -44,8 +44,7 @@ public: void setupConfigMap(const QStringList& argumentList); QVariant valueOrDefaultValueForKeyPath(const QString& keyPath); - QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); } - QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); } + QVariantMap& getSettingsMap() { return _configMap.getConfig(); } QVariantMap& getDescriptorsMap(); diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index 30eb200770..252079f182 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -111,7 +111,7 @@ void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentLi loadMapFromJSONFile(_masterConfig, masterConfigFilepath); } - // load the user config - that method replaces loadMasterAndUserConfig after the 1.7 migration + // load the user config - that method replace loadMasterAndUserConfig after the 1.7 migration loadConfig(argumentList); mergeMasterAndUserConfigs(); diff --git a/libraries/shared/src/HifiConfigVariantMap.h b/libraries/shared/src/HifiConfigVariantMap.h index 06e28cf182..cb6e92df96 100644 --- a/libraries/shared/src/HifiConfigVariantMap.h +++ b/libraries/shared/src/HifiConfigVariantMap.h @@ -15,6 +15,8 @@ #include #include +QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath, bool shouldCreateIfMissing = false); + class HifiConfigVariantMap { public: static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList); @@ -23,9 +25,12 @@ public: void loadMasterAndUserConfig(const QStringList& argumentList); void loadConfig(const QStringList& argumentList); - const QVariantMap& getMasterConfig() const { return _masterConfig; } - QVariantMap& getUserConfig() { return _userConfig; } + const QVariant value(const QString& key) const { return _userConfig.value(key); } + QVariant* valueForKeyPath(const QString& keyPath, bool shouldCreateIfMissing = false) + { return ::valueForKeyPath(_userConfig, keyPath, shouldCreateIfMissing); } + QVariantMap& getMergedConfig() { return _mergedConfig; } + QVariantMap& getConfig() { return _userConfig; } void mergeMasterAndUserConfigs(); @@ -41,6 +46,4 @@ private: void addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap); }; -QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath, bool shouldCreateIfMissing = false); - #endif // hifi_HifiConfigVariantMap_h From 4cdc98287bb110a1121ae22cb34c9563231bca7a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jul 2016 17:06:51 -0700 Subject: [PATCH 81/98] add canKick to USI, revert constants to production --- libraries/networking/src/LimitedNodeList.cpp | 4 ++++ libraries/networking/src/LimitedNodeList.h | 1 + .../script-engine/src/UsersScriptingInterface.cpp | 11 +++++++++++ libraries/script-engine/src/UsersScriptingInterface.h | 10 ++++++++++ 4 files changed, 26 insertions(+) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index b470c2fe25..431d372089 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -151,6 +151,10 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) { newPermissions.can(NodePermissions::Permission::canWriteToAssetServer)) { emit canWriteAssetsChanged(_permissions.can(NodePermissions::Permission::canWriteToAssetServer)); } + if (originalPermissions.can(NodePermissions::Permission::canKick) != + newPermissions.can(NodePermissions::Permission::canKick)) { + emit canKickChanged(_permissions.can(NodePermissions::Permission::canKick)); + } } QUdpSocket& LimitedNodeList::getDTLSSocket() { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 2b2f660569..48379b5e39 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -259,6 +259,7 @@ signals: void canRezChanged(bool canRez); void canRezTmpChanged(bool canRezTmp); void canWriteAssetsChanged(bool canWriteAssets); + void canKickChanged(bool canKick); protected slots: void connectedForLocalSocketTest(); diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 6be3aa39ac..69ad8e04ad 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -13,6 +13,12 @@ #include +UsersScriptingInterface::UsersScriptingInterface() { + // emit a signal when kick permissions have changed + auto nodeList = DependencyManager::get(); + connect(nodeList.data(), &LimitedNodeList::canKickChanged, this, &UsersScriptingInterface::canKickChanged); +} + void UsersScriptingInterface::ignore(const QUuid& nodeID) { // ask the NodeList to ignore this user (based on the session ID of their node) DependencyManager::get()->ignoreNodeBySessionID(nodeID); @@ -22,3 +28,8 @@ void UsersScriptingInterface::kick(const QUuid& nodeID) { // ask the NodeList to kick the user with the given session ID DependencyManager::get()->kickNodeBySessionID(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 c5929dc8b2..712eeedeb6 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -20,9 +20,19 @@ class UsersScriptingInterface : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY + Q_PROPERTY(bool canKick READ getCanKick) + +public: + UsersScriptingInterface(); + public slots: void ignore(const QUuid& nodeID); void kick(const QUuid& nodeID); + + bool getCanKick(); + +signals: + void canKickChanged(bool canKick); }; From 5eb890e4f5c85379d16de3939ddae35851341847 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jul 2016 13:26:25 -0700 Subject: [PATCH 82/98] add a simple IP address sanitizer for DS settings save --- domain-server/src/DomainServerSettingsManager.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index c47c786efa..dc49bc6126 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -495,7 +495,17 @@ void DomainServerSettingsManager::unpackPermissions() { needPack |= unpackPermissionsForKeypath(AGENT_PERMISSIONS_KEYPATH, &_agentPermissions); - needPack |= unpackPermissionsForKeypath(IP_PERMISSIONS_KEYPATH, &_ipPermissions); + needPack |= unpackPermissionsForKeypath(IP_PERMISSIONS_KEYPATH, &_ipPermissions, + [&](NodePermissionsPointer perms){ + // make sure that this permission row is for a valid IP address + if (QHostAddress(perms->getKey().first).isNull()) { + _ipPermissions.remove(perms->getKey()); + + // we removed a row from the IP permissions, we'll need a re-pack + needPack = true; + } + }); + needPack |= unpackPermissionsForKeypath(GROUP_PERMISSIONS_KEYPATH, &_groupPermissions, [&](NodePermissionsPointer perms){ From a15aef34dfc050faffcde02e7da9eeb39363ff8d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jul 2016 13:30:33 -0700 Subject: [PATCH 83/98] modify ignore script to handle kicking --- .../system/assets/images/ignore-target-01.svg | 37 ----- .../system/assets/images/ignore-target.svg | 52 +++++++ scripts/system/assets/images/kick-target.svg | 34 +++++ scripts/system/assets/images/tools/kick.svg | 140 ++++++++++++++++++ scripts/system/ignore.js | 23 ++- 5 files changed, 246 insertions(+), 40 deletions(-) delete mode 100644 scripts/system/assets/images/ignore-target-01.svg create mode 100644 scripts/system/assets/images/ignore-target.svg create mode 100644 scripts/system/assets/images/kick-target.svg create mode 100644 scripts/system/assets/images/tools/kick.svg diff --git a/scripts/system/assets/images/ignore-target-01.svg b/scripts/system/assets/images/ignore-target-01.svg deleted file mode 100644 index 98cee89ca1..0000000000 --- a/scripts/system/assets/images/ignore-target-01.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/scripts/system/assets/images/ignore-target.svg b/scripts/system/assets/images/ignore-target.svg new file mode 100644 index 0000000000..ca6aca3a5a --- /dev/null +++ b/scripts/system/assets/images/ignore-target.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/assets/images/kick-target.svg b/scripts/system/assets/images/kick-target.svg new file mode 100644 index 0000000000..e1ff64d272 --- /dev/null +++ b/scripts/system/assets/images/kick-target.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/scripts/system/assets/images/tools/kick.svg b/scripts/system/assets/images/tools/kick.svg new file mode 100644 index 0000000000..1eed6e7f43 --- /dev/null +++ b/scripts/system/assets/images/tools/kick.svg @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/ignore.js b/scripts/system/ignore.js index 1c996a7fcc..fac20eb02f 100644 --- a/scripts/system/ignore.js +++ b/scripts/system/ignore.js @@ -12,10 +12,14 @@ // grab the toolbar var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +function buttonImageURL() { + return Script.resolvePath("assets/images/tools/" + (Users.canKick ? 'kick.svg' : 'ignore.svg')); +} + // setup the ignore button and add it to the toolbar var button = toolbar.addButton({ objectName: 'ignore', - imageURL: Script.resolvePath("assets/images/tools/ignore.svg"), + imageURL: buttonImageURL(), visible: true, buttonState: 1, defaultState: 2, @@ -23,6 +27,11 @@ var button = toolbar.addButton({ alpha: 0.9 }); +// if this user's kick permissions change, change the state of the button in the HUD +Users.canKickChanged.connect(function(canKick){ + button.writeProperty('imageURL', buttonImageURL()); +}); + var isShowingOverlays = false; var ignoreOverlays = {}; @@ -54,6 +63,10 @@ function buttonClicked(){ button.clicked.connect(buttonClicked); +function overlayURL() { + return Script.resolvePath("assets/images/" + (Users.canKick ? "kick-target.svg" : "ignore-target.svg")); +} + function updateOverlays() { if (isShowingOverlays) { @@ -88,7 +101,7 @@ function updateOverlays() { } else { // add the overlay above this avatar var newOverlay = Overlays.addOverlay("image3d", { - url: Script.resolvePath("assets/images/ignore-target-01.svg"), + url: overlayURL(), position: overlayPosition, size: 0.4, scale: 0.4, @@ -130,7 +143,11 @@ function handleSelectedOverlay(clickedOverlay) { if (clickedOverlay.overlayID == ignoreOverlay) { // matched to an overlay, ask for the matching avatar to be ignored - Users.ignore(avatarID); + if (Users.canKick) { + Users.kick(avatarID); + } else { + Users.ignore(avatarID); + } // cleanup of the overlay is handled by the connection to avatarRemovedEvent } From 7d9937e412e81c89b5a75304cbff7a659facf061 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jul 2016 13:35:41 -0700 Subject: [PATCH 84/98] handle overlay change if permissions change --- scripts/system/ignore.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/ignore.js b/scripts/system/ignore.js index fac20eb02f..cae4e14638 100644 --- a/scripts/system/ignore.js +++ b/scripts/system/ignore.js @@ -96,7 +96,8 @@ function updateOverlays() { if (avatarID in ignoreOverlays) { // keep the overlay above the current position of this avatar Overlays.editOverlay(ignoreOverlays[avatarID], { - position: overlayPosition + position: overlayPosition, + url: overlayURL() }); } else { // add the overlay above this avatar From 6051fcd8f948686e408e91326e363a02763ff641 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jul 2016 14:37:09 -0700 Subject: [PATCH 85/98] use click instead of trigger, change targets to PNG --- .../system/assets/images/ignore-target.png | Bin 0 -> 8055 bytes .../system/assets/images/ignore-target.svg | 52 ------------------ scripts/system/assets/images/kick-target.png | Bin 0 -> 6540 bytes scripts/system/assets/images/kick-target.svg | 34 ------------ scripts/system/ignore.js | 40 ++++---------- 5 files changed, 12 insertions(+), 114 deletions(-) create mode 100644 scripts/system/assets/images/ignore-target.png delete mode 100644 scripts/system/assets/images/ignore-target.svg create mode 100644 scripts/system/assets/images/kick-target.png delete mode 100644 scripts/system/assets/images/kick-target.svg diff --git a/scripts/system/assets/images/ignore-target.png b/scripts/system/assets/images/ignore-target.png new file mode 100644 index 0000000000000000000000000000000000000000..697cbaeb6b66dc70e9cf1b198fe5f2d63f299b9d GIT binary patch literal 8055 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 literal 0 HcmV?d00001 diff --git a/scripts/system/assets/images/ignore-target.svg b/scripts/system/assets/images/ignore-target.svg deleted file mode 100644 index ca6aca3a5a..0000000000 --- a/scripts/system/assets/images/ignore-target.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/system/assets/images/kick-target.png b/scripts/system/assets/images/kick-target.png new file mode 100644 index 0000000000000000000000000000000000000000..491d5d8dc76fc64e11b9dc89412840f9bc6714b7 GIT binary patch literal 6540 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 literal 0 HcmV?d00001 diff --git a/scripts/system/assets/images/kick-target.svg b/scripts/system/assets/images/kick-target.svg deleted file mode 100644 index e1ff64d272..0000000000 --- a/scripts/system/assets/images/kick-target.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/scripts/system/ignore.js b/scripts/system/ignore.js index cae4e14638..ea53efb2ce 100644 --- a/scripts/system/ignore.js +++ b/scripts/system/ignore.js @@ -64,7 +64,7 @@ function buttonClicked(){ button.clicked.connect(buttonClicked); function overlayURL() { - return Script.resolvePath("assets/images/" + (Users.canKick ? "kick-target.svg" : "ignore-target.svg")); + return Script.resolvePath("assets/images/" + (Users.canKick ? "kick-target.png" : "ignore-target.png")); } function updateOverlays() { @@ -104,7 +104,7 @@ function updateOverlays() { var newOverlay = Overlays.addOverlay("image3d", { url: overlayURL(), position: overlayPosition, - size: 0.4, + size: 1, scale: 0.4, color: { red: 255, green: 255, blue: 255}, alpha: 1, @@ -179,14 +179,8 @@ Controller.mousePressEvent.connect(function(event){ // But we dont' get mousePressEvents. var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); -var TRIGGER_GRAB_VALUE = 0.85; // From handControllerGrab/Pointer.js. Should refactor. -var TRIGGER_ON_VALUE = 0.4; -var TRIGGER_OFF_VALUE = 0.15; -var triggered = false; -var activeHand = Controller.Standard.RightHand; - -function controllerComputePickRay() { - var controllerPose = Controller.getPoseValue(activeHand); +function controllerComputePickRay(hand) { + var controllerPose = Controller.getPoseValue(hand); if (controllerPose.valid && triggered) { var controllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, controllerPose.translation), MyAvatar.position); @@ -196,31 +190,21 @@ function controllerComputePickRay() { } } -function makeTriggerHandler(hand) { +function makeClickHandler(hand) { return function (value) { if (isShowingOverlays) { - if (!triggered && (value > TRIGGER_GRAB_VALUE)) { // should we smooth? - triggered = true; - if (activeHand !== hand) { - // No switching while the other is already triggered, so no need to release. - activeHand = (activeHand === Controller.Standard.RightHand) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; + var pickRay = controllerComputePickRay(hand); + if (pickRay) { + var overlayIntersection = Overlays.findRayIntersection(pickRay); + if (overlayIntersection.intersects) { + handleSelectedOverlay(overlayIntersection); } - - var pickRay = controllerComputePickRay(); - if (pickRay) { - var overlayIntersection = Overlays.findRayIntersection(pickRay); - if (overlayIntersection.intersects) { - handleSelectedOverlay(overlayIntersection); - } - } - } else if (triggered && (value < TRIGGER_OFF_VALUE)) { - triggered = false; } } }; } -triggerMapping.from(Controller.Standard.RT).peek().to(makeTriggerHandler(Controller.Standard.RightHand)); -triggerMapping.from(Controller.Standard.LT).peek().to(makeTriggerHandler(Controller.Standard.LeftHand)); +triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); +triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); triggerMapping.enable(); From 6d38301a3a00cb917033723ef74377d42a71c391 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jul 2016 15:03:33 -0700 Subject: [PATCH 86/98] rename ignore script to mod.js --- scripts/system/{ignore.js => mod.js} | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) rename scripts/system/{ignore.js => mod.js} (85%) diff --git a/scripts/system/ignore.js b/scripts/system/mod.js similarity index 85% rename from scripts/system/ignore.js rename to scripts/system/mod.js index ea53efb2ce..74888e7d12 100644 --- a/scripts/system/ignore.js +++ b/scripts/system/mod.js @@ -1,5 +1,5 @@ // -// ignore.js +// mod.js // scripts/system/ // // Created by Stephen Birarda on 07/11/2016 @@ -16,9 +16,9 @@ function buttonImageURL() { return Script.resolvePath("assets/images/tools/" + (Users.canKick ? 'kick.svg' : 'ignore.svg')); } -// setup the ignore button and add it to the toolbar +// setup the mod button and add it to the toolbar var button = toolbar.addButton({ - objectName: 'ignore', + objectName: 'mod', imageURL: buttonImageURL(), visible: true, buttonState: 1, @@ -33,18 +33,18 @@ Users.canKickChanged.connect(function(canKick){ }); var isShowingOverlays = false; -var ignoreOverlays = {}; +var modOverlays = {}; function removeOverlays() { // enumerate the overlays and remove them - var ignoreOverlayKeys = Object.keys(ignoreOverlays); + var modOverlayKeys = Object.keys(modOverlays); - for (i = 0; i < ignoreOverlayKeys.length; ++i) { - var avatarID = ignoreOverlayKeys[i]; - Overlays.deleteOverlay(ignoreOverlays[avatarID]); + for (i = 0; i < modOverlayKeys.length; ++i) { + var avatarID = modOverlayKeys[i]; + Overlays.deleteOverlay(modOverlays[avatarID]); } - ignoreOverlays = {}; + modOverlays = {}; } // handle clicks on the toolbar button @@ -93,9 +93,9 @@ function updateOverlays() { var overlayPosition = avatar.getJointPosition("Head"); overlayPosition.y += 0.45; - if (avatarID in ignoreOverlays) { + if (avatarID in modOverlays) { // keep the overlay above the current position of this avatar - Overlays.editOverlay(ignoreOverlays[avatarID], { + Overlays.editOverlay(modOverlays[avatarID], { position: overlayPosition, url: overlayURL() }); @@ -114,7 +114,7 @@ function updateOverlays() { }); // push this overlay to our array of overlays - ignoreOverlays[avatarID] = newOverlay; + modOverlays[avatarID] = newOverlay; } } } @@ -127,23 +127,23 @@ AvatarList.avatarRemovedEvent.connect(function(avatarID){ // we are currently showing overlays and an avatar just went away // first remove the rendered overlay - Overlays.deleteOverlay(ignoreOverlays[avatarID]); + Overlays.deleteOverlay(modOverlays[avatarID]); - // delete the saved ID of the overlay from our ignored overlays object - delete ignoreOverlays[avatarID]; + // delete the saved ID of the overlay from our mod overlays object + delete modOverlays[avatarID]; } }); function handleSelectedOverlay(clickedOverlay) { - // see this is one of our ignore overlays + // see this is one of our mod overlays - var ignoreOverlayKeys = Object.keys(ignoreOverlays) - for (i = 0; i < ignoreOverlayKeys.length; ++i) { - var avatarID = ignoreOverlayKeys[i]; - var ignoreOverlay = ignoreOverlays[avatarID]; + var modOverlayKeys = Object.keys(modOverlays) + for (i = 0; i < modOverlayKeys.length; ++i) { + var avatarID = modOverlayKeys[i]; + var modOverlay = modOverlays[avatarID]; - if (clickedOverlay.overlayID == ignoreOverlay) { - // matched to an overlay, ask for the matching avatar to be ignored + if (clickedOverlay.overlayID == modOverlay) { + // matched to an overlay, ask for the matching avatar to be kicked or ignored if (Users.canKick) { Users.kick(avatarID); } else { @@ -210,7 +210,7 @@ triggerMapping.enable(); // cleanup the toolbar button and overlays when script is stopped Script.scriptEnding.connect(function() { - toolbar.removeButton('ignore'); + toolbar.removeButton('mod'); removeOverlays(); triggerMapping.disable(); }); From 6e50e110027390d4821a64447d7b14a22e1be819 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jul 2016 15:04:27 -0700 Subject: [PATCH 87/98] replace ignore in default scripts with mod --- scripts/defaultScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 817d63582d..0efcd0c140 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -17,7 +17,7 @@ Script.load("system/goto.js"); Script.load("system/hmd.js"); Script.load("system/marketplace.js"); Script.load("system/edit.js"); -Script.load("system/ignore.js"); +Script.load("system/mod.js"); Script.load("system/selectAudioDevice.js"); Script.load("system/notifications.js"); Script.load("system/controllers/handControllerGrab.js"); From 760223a550021c511ea3e7baaafc4376c84c72eb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jul 2016 18:04:47 -0700 Subject: [PATCH 88/98] check for existing public key with lowercase username --- domain-server/src/DomainGatekeeper.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index eb907aace4..690c11e07f 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -453,11 +453,11 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node bool DomainGatekeeper::verifyUserSignature(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr) { - // it's possible this user can be allowed to connect, but we need to check their username signature - QByteArray publicKeyArray = _userPublicKeys.value(username.toLower()); + auto lowerUsername = username.toLower(); + QByteArray publicKeyArray = _userPublicKeys.value(lowerUsername); - const QUuid& connectionToken = _connectionTokenHash.value(username.toLower()); + const QUuid& connectionToken = _connectionTokenHash.value(lowerUsername); if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) { // if we do have a public key for the user, check for a signature match @@ -467,8 +467,8 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, // first load up the public key into an RSA struct RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size()); - QByteArray lowercaseUsername = username.toLower().toUtf8(); - QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()), + QByteArray lowercaseUsernameUTF8 = lowerUsername.toUtf8(); + QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsernameUTF8.append(connectionToken.toRfc4122()), QCryptographicHash::Sha256); if (rsaPublicKey) { @@ -598,10 +598,7 @@ void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) { QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); QString username = extractUsernameFromPublicKeyRequest(requestReply); - if (jsonObject["status"].toString() == "success" && username != "") { - // figure out which user this is for - const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key"; - qDebug() << "Storing a public key for user" << username; + if (jsonObject["status"].toString() == "success" && !username.isEmpty()) { // pull the public key as a QByteArray from this response const QString JSON_DATA_KEY = "data"; const QString JSON_PUBLIC_KEY_KEY = "public_key"; From f02f6195975178290de3cfc5b92c8311cb2fee0d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 Jul 2016 10:21:17 -0700 Subject: [PATCH 89/98] use boolean from RTClick and LTClick --- scripts/system/mod.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/mod.js b/scripts/system/mod.js index 74888e7d12..f381ab3703 100644 --- a/scripts/system/mod.js +++ b/scripts/system/mod.js @@ -191,8 +191,8 @@ function controllerComputePickRay(hand) { } function makeClickHandler(hand) { - return function (value) { - if (isShowingOverlays) { + return function(clicked) { + if (clicked && isShowingOverlays) { var pickRay = controllerComputePickRay(hand); if (pickRay) { var overlayIntersection = Overlays.findRayIntersection(pickRay); From fa9199c419e237207628730c6be0cf8eceb0246b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 Jul 2016 10:25:36 -0700 Subject: [PATCH 90/98] use float for clicked value in mod.js --- scripts/system/mod.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/mod.js b/scripts/system/mod.js index f381ab3703..f60ad7b35c 100644 --- a/scripts/system/mod.js +++ b/scripts/system/mod.js @@ -192,7 +192,7 @@ function controllerComputePickRay(hand) { function makeClickHandler(hand) { return function(clicked) { - if (clicked && isShowingOverlays) { + if (clicked == 1.0 && isShowingOverlays) { var pickRay = controllerComputePickRay(hand); if (pickRay) { var overlayIntersection = Overlays.findRayIntersection(pickRay); From 6196f5e86f739d5587959b6b62af37f772bf5407 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 Jul 2016 12:04:28 -0700 Subject: [PATCH 91/98] switch back to SVGs, remove triggered for controller pose --- .../system/assets/images/ignore-target.png | Bin 8055 -> 0 bytes .../system/assets/images/ignore-target.svg | 53 ++++++++++++++++++ scripts/system/assets/images/kick-target.png | Bin 6540 -> 0 bytes scripts/system/assets/images/kick-target.svg | 33 +++++++++++ scripts/system/mod.js | 4 +- 5 files changed, 88 insertions(+), 2 deletions(-) delete mode 100644 scripts/system/assets/images/ignore-target.png create mode 100644 scripts/system/assets/images/ignore-target.svg delete mode 100644 scripts/system/assets/images/kick-target.png create mode 100644 scripts/system/assets/images/kick-target.svg diff --git a/scripts/system/assets/images/ignore-target.png b/scripts/system/assets/images/ignore-target.png deleted file mode 100644 index 697cbaeb6b66dc70e9cf1b198fe5f2d63f299b9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8055 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 diff --git a/scripts/system/assets/images/ignore-target.svg b/scripts/system/assets/images/ignore-target.svg new file mode 100644 index 0000000000..3d685139ec --- /dev/null +++ b/scripts/system/assets/images/ignore-target.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/assets/images/kick-target.png b/scripts/system/assets/images/kick-target.png deleted file mode 100644 index 491d5d8dc76fc64e11b9dc89412840f9bc6714b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6540 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 diff --git a/scripts/system/assets/images/kick-target.svg b/scripts/system/assets/images/kick-target.svg new file mode 100644 index 0000000000..21cb3a5462 --- /dev/null +++ b/scripts/system/assets/images/kick-target.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + diff --git a/scripts/system/mod.js b/scripts/system/mod.js index f60ad7b35c..035d7726a1 100644 --- a/scripts/system/mod.js +++ b/scripts/system/mod.js @@ -64,7 +64,7 @@ function buttonClicked(){ button.clicked.connect(buttonClicked); function overlayURL() { - return Script.resolvePath("assets/images/" + (Users.canKick ? "kick-target.png" : "ignore-target.png")); + return Script.resolvePath("assets") + "/images/" + (Users.canKick ? "kick-target.svg" : "ignore-target.svg"); } function updateOverlays() { @@ -181,7 +181,7 @@ var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); function controllerComputePickRay(hand) { var controllerPose = Controller.getPoseValue(hand); - if (controllerPose.valid && triggered) { + if (controllerPose.valid) { var controllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, controllerPose.translation), MyAvatar.position); // This gets point direction right, but if you want general quaternion it would be more complicated: From a1220b87f13f57058b07bced6afca403a86634b8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 29 Jul 2016 15:53:45 -0700 Subject: [PATCH 92/98] set key ID for verified user in right place --- domain-server/src/DomainGatekeeper.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 690c11e07f..8f8c8e001c 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -147,7 +147,6 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin #endif } } else { - userPerms.setID(verifiedUsername); if (_server->_settingsManager.havePermissionsForName(verifiedUsername)) { userPerms = _server->_settingsManager.getPermissionsForName(verifiedUsername); #ifdef WANT_DEBUG @@ -161,7 +160,6 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin qDebug() << "| user-permissions: specific IP matches, so:" << userPerms; #endif } else { - // they are logged into metaverse, but we don't have specific permissions for them. userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn); #ifdef WANT_DEBUG @@ -208,6 +206,7 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin } } + userPerms.setID(verifiedUsername); userPerms.setVerifiedUserName(verifiedUsername); } From 38ba54e48161ac416b964b9d9c38b52bf459c58e Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Tue, 2 Aug 2016 16:04:54 -0700 Subject: [PATCH 93/98] Fix confirm dialog --- libraries/ui/src/OffscreenUi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 36297d76c7..bc0a6bf544 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -107,7 +107,7 @@ public: QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); static QMessageBox::StandardButton question(const QString& title, const QString& text, - QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); static QMessageBox::StandardButton warning(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, From 79a243b1713859ee973262e00a3d4b56ae0ec8f7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Aug 2016 16:19:38 -0700 Subject: [PATCH 94/98] Clean up progress.js --- scripts/system/progress.js | 79 ++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/scripts/system/progress.js b/scripts/system/progress.js index faf8d1bd38..821fc872b7 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -13,6 +13,11 @@ (function() { + function debug() { + return; + print.apply(null, arguments); + } + var rawProgress = 100, // % raw value. displayProgress = 100, // % smoothed value to display. DISPLAY_PROGRESS_MINOR_MAXIMUM = 8, // % displayed progress bar goes up to while 0% raw progress. @@ -39,7 +44,6 @@ background2D = {}, bar2D = {}, SCALE_2D = 0.35, // Scale the SVGs for 2D display. - //SCALE_2D = 1.0, // Scale the SVGs for 2D display. background3D = {}, bar3D = {}, PROGRESS_3D_DIRECTION = 0.0, // Degrees from avatar orientation. @@ -96,44 +100,58 @@ }); } function resetProgress() { - wasActive = true; + isDownloading = true; bestRawProgress = 0; rawProgress = 0; - cooldown = 1000; + initialDelayCooldown = INITIAL_DELAY_COOLDOWN_TIME; displayProgress = 0; } Window.domainChanged.connect(function() { - hasShownOnThisDomain = false; resetProgress(); }); - var hasShownOnThisDomain = false; - + // Max seen since downloads started. This is reset when all downloads have completed. var maxSeen = 0; + + // Progress is defined as: (pending_downloads + active_downloads) / max_seen + // We keep track of both the current progress (rawProgress) and the + // best progress we've seen (bestRawProgress). As you are downloading, you may + // encounter new assets that require downloads, increasing the number of + // pending downloads and thus decreasing your overall progress. var bestRawProgress = 0; - var wasActive = false; - var cooldown = 1000; + + // True if we have known active downloads + var isDownloading = false; + + // Entities are streamed to users, so you don't receive them all at once; instead, you + // receive them over a period of time. In many cases we end up in a situation where + // + // The initial delay cooldown keeps us from tracking progress before the allotted time + // has passed. + var INITIAL_DELAY_COOLDOWN_TIME = 1000; + var initialDelayCooldown = 0; function onDownloadInfoChanged(info) { var i; - print("PROGRESS: Download info changed ", info.downloading.length, info.pending, maxSeen); + debug("PROGRESS: Download info changed ", info.downloading.length, info.pending, maxSeen); + // Update raw progress value if (info.downloading.length + info.pending === 0) { - wasActive = false; + isDownloading = false; rawProgress = 100; bestRawProgress = 100; - cooldown = 0; + initialDelayCooldown = INITIAL_DELAY_COOLDOWN_TIME; } else { var count = info.downloading.length + info.pending; - if (!wasActive) { + if (!isDownloading) { resetProgress(); maxSeen = count; } if (count > maxSeen) { maxSeen = count; } - if (cooldown <= 0) { + if (initialDelayCooldown <= 0) { rawProgress = ((maxSeen - count) / maxSeen) * 100; if (rawProgress > bestRawProgress) { @@ -141,7 +159,7 @@ } } } - print("PROGRESS:", rawProgress, bestRawProgress, maxSeen); + debug("PROGRESS:", rawProgress, bestRawProgress, maxSeen); } function createOverlays() { @@ -214,7 +232,7 @@ var worldOverlayOn = false; var currentOrientation = null; function update() { - cooldown -= 30; + initialDelayCooldown -= 30; var viewport, eyePosition, avatarOrientation; @@ -231,7 +249,6 @@ displayProgress += diff * 0.05; } } - //print('PROGRESS:', displayProgress); // Update state if (!visible) { // Not visible because no recent downloads @@ -328,16 +345,7 @@ windowHeight = viewport.y; var yOffset = HMD.active ? -300 : -10; - yOffset += yAdjust; - // if (hmdActive) { - // if (true) { //hasShownOnThisDomain) { - // yOffset = -100; - // SCALE_2D = 1.0/3; - // } else { - // yOffset = -300; - // SCALE_2D = 2.0/3; - // } - // } + background2D.width = SCALE_2D * BACKGROUND_WIDTH; background2D.height = SCALE_2D * BACKGROUND_HEIGHT; bar2D.width = SCALE_2D * BAR_WIDTH; @@ -354,9 +362,6 @@ }); } - function setProgressBar(progress) { - } - function setUp() { background2D.width = SCALE_2D * BACKGROUND_WIDTH; background2D.height = SCALE_2D * BACKGROUND_HEIGHT; @@ -384,25 +389,9 @@ deleteOverlays(); } - var yAdjust = 0; - function keyPress(event) { - print("Key event: ", event.text); - if (event.text == '.') { - yAdjust -= 10; - updateProgressBarLocation(); - } else if (event.text == ',') { - yAdjust += 10; - updateProgressBarLocation(); - } else if (event.text == 'SPACE') { - worldOverlayOn = !worldOverlayOn; - } - } - setUp(); GlobalServices.downloadInfoChanged.connect(onDownloadInfoChanged); GlobalServices.updateDownloadInfo(); - //Script.update.connect(update); Script.setInterval(update, 1000/60); Script.scriptEnding.connect(tearDown); - Controller.keyPressEvent.connect(keyPress); }()); From 1f2e34d59b85c3a265f7955401c112f396914888 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Aug 2016 16:29:40 -0700 Subject: [PATCH 95/98] Remove 3d overlay from progress.js --- scripts/system/progress.js | 155 +++++-------------------------------- 1 file changed, 21 insertions(+), 134 deletions(-) diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 821fc872b7..67ef1e203c 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -38,31 +38,11 @@ BACKGROUND_WIDTH = 520, BACKGROUND_HEIGHT = 50, BACKGROUND_URL = Script.resolvePath("assets/images/progress-bar-background.svg"), - use3DOverlay = false, windowWidth = 0, windowHeight = 0, background2D = {}, bar2D = {}, - SCALE_2D = 0.35, // Scale the SVGs for 2D display. - background3D = {}, - bar3D = {}, - PROGRESS_3D_DIRECTION = 0.0, // Degrees from avatar orientation. - PROGRESS_3D_DISTANCE = 0.602, // Horizontal distance from avatar position. - PROGRESS_3D_ELEVATION = -0.8, // Height of top middle of top notification relative to avatar eyes. - PROGRESS_3D_ELEVATION = -0.2, // Height of top middle of top notification relative to avatar eyes. - PROGRESS_3D_YAW = 0.0, // Degrees relative to notifications direction. - PROGRESS_3D_PITCH = -0.0, // Degrees from vertical. - SCALE_3D = 0.0004, // Scale the bar SVG for 3D display. - BACKGROUND_3D_SIZE = { - x: 0.76, - y: 0.08 - }, // Match up with the 3D background with those of notifications.js notices. - BACKGROUND_3D_COLOR = { - red: 2, - green: 2, - blue: 2 - }, - BACKGROUND_3D_ALPHA = 1.0; + SCALE_2D = 0.35; // Scale the SVGs for 2D display. function fade() { @@ -83,32 +63,21 @@ visible = false; } - if (use3DOverlay) { - Overlays.editOverlay(background3D.overlay, { - alpha: alpha, - visible: visible - }); - } else { - Overlays.editOverlay(background2D.overlay, { - alpha: alpha, - visible: visible - }); - } - Overlays.editOverlay(use3DOverlay ? bar3D.overlay : bar2D.overlay, { + Overlays.editOverlay(background2D.overlay, { + alpha: alpha, + visible: visible + }); + Overlays.editOverlay(bar2D.overlay, { alpha: alpha, visible: visible }); } - function resetProgress() { - isDownloading = true; - bestRawProgress = 0; - rawProgress = 0; - initialDelayCooldown = INITIAL_DELAY_COOLDOWN_TIME; - displayProgress = 0; - } Window.domainChanged.connect(function() { - resetProgress(); + isDownloading = false; + bestRawProgress = 100; + rawProgress = 100; + displayProgress = 100; }); // Max seen since downloads started. This is reset when all downloads have completed. @@ -145,7 +114,11 @@ } else { var count = info.downloading.length + info.pending; if (!isDownloading) { - resetProgress(); + isDownloading = true; + bestRawProgress = 0; + rawProgress = 0; + initialDelayCooldown = INITIAL_DELAY_COOLDOWN_TIME; + displayProgress = 0; maxSeen = count; } if (count > maxSeen) { @@ -163,41 +136,6 @@ } function createOverlays() { - background3D.overlay = Overlays.addOverlay("image3d", { - url: BACKGROUND_URL, - subImage: { - x: 0, - y: 0, - width: BACKGROUND_WIDTH, - height: BACKGROUND_HEIGHT - }, - scale: SCALE_3D * BACKGROUND_WIDTH, - isFacingAvatar: false, - visible: false, - alpha: 1.0, - ignoreRayIntersection: true, - emissive: true, - isFacingAvatar: true, - drawInFront: true - }); - bar3D.overlay = Overlays.addOverlay("image3d", { - url: BAR_URL, - subImage: { - x: 0, - y: 0, - width: BAR_WIDTH, - height: BAR_HEIGHT - }, - scale: SCALE_3D * BAR_WIDTH, - isFacingAvatar: false, - visible: false, - alpha: 1.0, - ignoreRayIntersection: true, - emissive: true, - isFacingAvatar: true, - drawInFront: true - }); - background2D.overlay = Overlays.addOverlay("image", { imageURL: BACKGROUND_URL, width: background2D.width, @@ -221,15 +159,11 @@ } function deleteOverlays() { - Overlays.deleteOverlay(background3D.overlay); - Overlays.deleteOverlay(bar3D.overlay); - Overlays.deleteOverlay(background2D.overlay); Overlays.deleteOverlay(bar2D.overlay); } var b = 0; - var worldOverlayOn = false; var currentOrientation = null; function update() { initialDelayCooldown -= 30; @@ -237,10 +171,6 @@ eyePosition, avatarOrientation; - if (use3DOverlay !== worldOverlayOn) { - use3DOverlay = !use3DOverlay; - } - if (displayProgress < rawProgress) { var diff = rawProgress - displayProgress; if (diff < 0.5) { @@ -284,18 +214,10 @@ } } - if (use3DOverlay) { - Overlays.editOverlay(background2D.overlay, { visible: false }); - Overlays.editOverlay(bar2D.overlay, { visible: false }); - } else { - Overlays.editOverlay(background3D.overlay, { visible: false }); - Overlays.editOverlay(bar3D.overlay, { visible: false }); - } - if (visible) { // Update progress bar - Overlays.editOverlay(use3DOverlay ? bar3D.overlay : bar2D.overlay, { + Overlays.editOverlay(bar2D.overlay, { visible: true, subImage: { x: BAR_WIDTH * (1 - displayProgress / 100), @@ -305,36 +227,15 @@ }, }); - Overlays.editOverlay(use3DOverlay ? background3D.overlay : background2D.overlay, { + Overlays.editOverlay(background2D.overlay, { visible: true, }); - // Update position - if (use3DOverlay) { - print("HERE"); - // Update 3D overlays to maintain positions relative to avatar - eyePosition = MyAvatar.getDefaultEyePosition(); - avatarOrientation = Camera.orientation; + // Update 2D overlays to maintain positions at bottom middle of window + viewport = Controller.getViewportDimensions(); - currentOrientation = Quat.slerp(currentOrientation, avatarOrientation, 0.10); - avatarOrientation = currentOrientation; - - Overlays.editOverlay(background3D.overlay, { - position: Vec3.sum(eyePosition, Vec3.multiplyQbyV(avatarOrientation, background3D.offset)), - //rotation: Quat.multiply(avatarOrientation, background3D.orientation) - }); - Overlays.editOverlay(bar3D.overlay, { - position: Vec3.sum(eyePosition, Vec3.multiplyQbyV(avatarOrientation, bar3D.offset)), - //rotation: Quat.multiply(avatarOrientation, bar3D.orientation) - }); - - } else { - // Update 2D overlays to maintain positions at bottom middle of window - viewport = Controller.getViewportDimensions(); - - if (viewport.x !== windowWidth || viewport.y !== windowHeight) { - updateProgressBarLocation(); - } + if (viewport.x !== windowWidth || viewport.y !== windowHeight) { + updateProgressBarLocation(); } } } @@ -368,20 +269,6 @@ bar2D.width = SCALE_2D * BAR_WIDTH; bar2D.height = SCALE_2D * BAR_HEIGHT; - background3D.offset = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, PROGRESS_3D_DIRECTION, 0), { - x: 0, - y: 0, - z: -PROGRESS_3D_DISTANCE - }); - background3D.offset.y += PROGRESS_3D_ELEVATION; - background3D.orientation = Quat.fromPitchYawRollDegrees(PROGRESS_3D_PITCH, PROGRESS_3D_DIRECTION + PROGRESS_3D_YAW, 0); - bar3D.offset = Vec3.sum(background3D.offset, { - x: 0, - y: 0, - z: 0.001 - }); // Just in front of background - bar3D.orientation = background3D.orientation; - createOverlays(); } From 5d3e125a0668d57a34bae8f35fb83303d6a2b21a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Aug 2016 16:32:41 -0700 Subject: [PATCH 96/98] make failed geomtetry request not hold physics --- .../src/RenderableModelEntityItem.cpp | 10 ++++++++++ .../entities-renderer/src/RenderableModelEntityItem.h | 2 ++ .../src/model-networking/ModelCache.cpp | 2 ++ .../model-networking/src/model-networking/ModelCache.h | 3 +++ libraries/render-utils/src/Model.cpp | 7 ++++++- libraries/render-utils/src/Model.h | 8 +++++++- 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 4e8ecf3054..564a58708f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -916,6 +916,16 @@ bool RenderableModelEntityItem::contains(const glm::vec3& point) const { return false; } +bool RenderableModelEntityItem::shouldBePhysical() const { + // If we have a model, make sure it hasn't failed to download. + // If it has, we'll report back that we shouldn't be physical so that physics aren't held waiting for us to be ready. + if (_model && _model->didGeometryRequestFail()) { + return false; + } else { + return ModelEntityItem::shouldBePhysical(); + } +} + glm::quat RenderableModelEntityItem::getAbsoluteJointRotationInObjectFrame(int index) const { if (_model) { glm::quat result; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 339c907532..f487e79880 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -65,6 +65,8 @@ public: virtual bool contains(const glm::vec3& point) const override; + virtual bool shouldBePhysical() const override; + // these are in the frame of this object (model space) virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 26798070a6..c4b2a6dd22 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -403,6 +403,8 @@ void GeometryResourceWatcher::setResource(GeometryResource::Pointer resource) { void GeometryResourceWatcher::resourceFinished(bool success) { if (success) { _geometryRef = std::make_shared(*_resource); + } else { + emit resourceFailed(); } } diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 4a0a921a04..62037d67bc 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -111,6 +111,9 @@ public: QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); } +signals: + void resourceFailed(); + private: void startWatching(); void stopWatching(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b04a1d8023..581bd285e2 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -102,13 +102,17 @@ Model::Model(RigPointer rig, QObject* parent) : _calculatedMeshTrianglesValid(false), _meshGroupsKnown(false), _isWireframe(false), - _rig(rig) { + _rig(rig) +{ // we may have been created in the network thread, but we live in the main thread if (_viewState) { moveToThread(_viewState->getMainThread()); } setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); + + // handle download failure reported by the GeometryResourceWatcher + connect(&_renderWatcher, &GeometryResourceWatcher::resourceFailed, this, &Model::handleGeometryResourceFailure); } Model::~Model() { @@ -818,6 +822,7 @@ void Model::setURL(const QUrl& url) { _needsReload = true; _needsUpdateTextures = true; _meshGroupsKnown = false; + _geometryRequestFailed = false; invalidCalculatedMeshBoxes(); deleteGeometry(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 98e50c66f4..b95c0318b4 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -147,8 +147,9 @@ public: Q_INVOKABLE void setCollisionModelURL(const QUrl& url); const QUrl& getCollisionURL() const { return _collisionUrl; } - bool isActive() const { return isLoaded(); } + + bool didGeometryRequestFail() const { return _geometryRequestFailed; } bool convexHullContains(glm::vec3 point); @@ -392,6 +393,11 @@ protected: RigPointer _rig; uint32_t _deleteGeometryCounter { 0 }; + + bool _geometryRequestFailed { false }; + +private slots: + void handleGeometryResourceFailure() { _geometryRequestFailed = true; } }; Q_DECLARE_METATYPE(ModelPointer) From adbd9ff85428bc99fcb28cc8db6bf190b0f0eb4e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Aug 2016 16:34:57 -0700 Subject: [PATCH 97/98] Add constants for progress bar offset --- scripts/system/progress.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 67ef1e203c..7c1c475e99 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -34,6 +34,8 @@ visible = false, BAR_WIDTH = 480, // Dimension of SVG in pixels of visible portion (half) of the bar. BAR_HEIGHT = 10, + BAR_Y_OFFSET_2D = -10, // Offset of progress bar while in desktop mode + BAR_Y_OFFSET_HMD = -300, // Offset of progress bar while in HMD BAR_URL = Script.resolvePath("assets/images/progress-bar.svg"), BACKGROUND_WIDTH = 520, BACKGROUND_HEIGHT = 50, @@ -245,7 +247,7 @@ windowWidth = viewport.x; windowHeight = viewport.y; - var yOffset = HMD.active ? -300 : -10; + var yOffset = HMD.active ? BAR_Y_OFFSET_HMD : BAR_Y_OFFSET_2D; background2D.width = SCALE_2D * BACKGROUND_WIDTH; background2D.height = SCALE_2D * BACKGROUND_HEIGHT; From 3c30743d0dcc8f3e1d7c538cdf86f223914d8686 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Tue, 2 Aug 2016 16:48:39 -0700 Subject: [PATCH 98/98] Remove url change debug info for WebView --- interface/resources/qml/controls-uit/WebView.qml | 1 - interface/resources/qml/controls/WebView.qml | 1 - 2 files changed, 2 deletions(-) diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index 4c165fc587..b599e29fe0 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -35,7 +35,6 @@ WebEngineView { } onUrlChanged: { - console.log("Url changed to " + url); var originalUrl = url.toString(); newUrl = urlHandler.fixupUrl(originalUrl).toString(); if (newUrl !== originalUrl) { diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 2f94740fe6..bba91c64f6 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -26,7 +26,6 @@ WebEngineView { } onUrlChanged: { - console.log("Url changed to " + url); var originalUrl = url.toString(); newUrl = urlHandler.fixupUrl(originalUrl).toString(); if (newUrl !== originalUrl) {