mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
commit
9962d47173
28 changed files with 775 additions and 327 deletions
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": 1.5,
|
||||
"version": 1.7,
|
||||
"settings": [
|
||||
{
|
||||
"name": "metaverse",
|
||||
|
@ -395,7 +395,7 @@
|
|||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
|
||||
"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 <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"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 <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 6
|
||||
"span": 7
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -635,23 +649,29 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_kick",
|
||||
"label": "Kick Users",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "permissions",
|
||||
"name": "ip_permissions",
|
||||
"type": "table",
|
||||
"caption": "Permissions for Specific Users",
|
||||
"caption": "Permissions for Users from IP Addresses",
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "User",
|
||||
"label": "IP Address",
|
||||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"span": 6
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 7
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -701,6 +721,86 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_kick",
|
||||
"label": "Kick Users",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "permissions",
|
||||
"type": "table",
|
||||
"caption": "Permissions for Specific Users",
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "User",
|
||||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"span": 7
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
"label": ""
|
||||
},
|
||||
{
|
||||
"name": "id_can_connect",
|
||||
"label": "Connect",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_adjust_locks",
|
||||
"label": "Lock / Unlock",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez",
|
||||
"label": "Rez",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_tmp",
|
||||
"label": "Rez Temporary",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_write_to_asset_server",
|
||||
"label": "Write Assets",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_connect_past_max_capacity",
|
||||
"label": "Ignore Max Capacity",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_kick",
|
||||
"label": "Kick Users",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -75,15 +75,6 @@ span.port {
|
|||
color: #666666;
|
||||
}
|
||||
|
||||
.locked {
|
||||
color: #428bca;
|
||||
}
|
||||
|
||||
.locked-table {
|
||||
cursor: not-allowed;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.advanced-setting {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -57,15 +57,13 @@
|
|||
<div class="panel-body">
|
||||
<% _.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) %>
|
||||
<% }); %>
|
||||
<% }%>
|
||||
</div>
|
||||
|
|
|
@ -41,7 +41,7 @@ var Settings = {
|
|||
};
|
||||
|
||||
var viewHelpers = {
|
||||
getFormGroup: function(keypath, setting, values, isAdvanced, isLocked) {
|
||||
getFormGroup: function(keypath, setting, values, isAdvanced) {
|
||||
form_group = "<div class='form-group " + (isAdvanced ? Settings.ADVANCED_CLASS : "") + "' data-keypath='" + keypath + "'>";
|
||||
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 += "<label class='" + label_class + "'>" + setting.label + "</label>"
|
||||
}
|
||||
|
||||
form_group += "<div class='toggle-checkbox-container" + (isLocked ? " disabled" : "") + "'>"
|
||||
form_group += "<input type='checkbox'" + common_attrs('toggle-checkbox') + (setting_value ? "checked" : "")
|
||||
form_group += (isLocked ? " disabled" : "") + "/>"
|
||||
form_group += "<div class='toggle-checkbox-container'>"
|
||||
form_group += "<input type='checkbox'" + common_attrs('toggle-checkbox') + (setting_value ? "checked" : "") + "/>"
|
||||
|
||||
if (setting.help) {
|
||||
form_group += "<span class='help-block checkbox-help'>" + setting.help + "</span>";
|
||||
|
@ -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 += "<select class='form-control' data-hidden-input='" + keypath + "'>'"
|
||||
|
@ -107,12 +103,10 @@ var viewHelpers = {
|
|||
|
||||
if (setting.href) {
|
||||
form_group += "<a href='" + setting.href + "'style='display: block;' role='button'"
|
||||
+ (isLocked ? " disabled" : "")
|
||||
+ common_attrs("btn " + setting.classes) + " target='_blank'>"
|
||||
+ setting.button_label + "</a>";
|
||||
} else {
|
||||
form_group += "<button " + common_attrs("btn " + setting.classes)
|
||||
+ (isLocked ? " disabled" : "") + ">"
|
||||
form_group += "<button " + common_attrs("btn " + setting.classes) + ">"
|
||||
+ setting.button_label + "</button>";
|
||||
}
|
||||
|
||||
|
@ -124,7 +118,7 @@ var viewHelpers = {
|
|||
|
||||
form_group += "<input type='" + input_type + "'" + common_attrs() +
|
||||
"placeholder='" + (_.has(setting, 'placeholder') ? setting.placeholder : "") +
|
||||
"' value='" + setting_value + "'" + (isLocked ? " disabled" : "") + "/>"
|
||||
"' value='" + setting_value + "'/>"
|
||||
}
|
||||
|
||||
form_group += "<span class='help-block'>" + setting.help + "</span>"
|
||||
|
@ -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 += "<table class='table table-bordered " + (isLocked ? "locked-table" : "") + "' " +
|
||||
html += "<table class='table table-bordered' " +
|
||||
"data-short-name='" + setting.name + "' name='" + keypath + "' " +
|
||||
"id='" + (!_.isUndefined(setting.html_id) ? setting.html_id : keypath) + "' " +
|
||||
"data-setting-type='" + (isArray ? 'array' : 'hash') + "'>";
|
||||
|
@ -976,7 +960,7 @@ function makeTable(setting, keypath, setting_value, isLocked) {
|
|||
_.each(setting.groups, function (group) {
|
||||
html += "<td colspan='" + group.span + "'><strong>" + group.label + "</strong></td>"
|
||||
})
|
||||
if (!isLocked && !setting.read_only) {
|
||||
if (!setting.read_only) {
|
||||
if (setting.can_order) {
|
||||
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES +
|
||||
"'><a href='javascript:void(0);' class='glyphicon glyphicon-sort'></a></td>";
|
||||
|
@ -1004,7 +988,7 @@ function makeTable(setting, keypath, setting_value, isLocked) {
|
|||
(col.class ? col.class : '') + "'><strong>" + col.label + "</strong></td>" // Data
|
||||
})
|
||||
|
||||
if (!isLocked && !setting.read_only) {
|
||||
if (!setting.read_only) {
|
||||
if (setting.can_order) {
|
||||
numVisibleColumns++;
|
||||
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES +
|
||||
|
@ -1083,7 +1067,7 @@ function makeTable(setting, keypath, setting_value, isLocked) {
|
|||
|
||||
});
|
||||
|
||||
if (!isLocked && !setting.read_only) {
|
||||
if (!setting.read_only) {
|
||||
if (setting.can_order) {
|
||||
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES+
|
||||
"'><a href='javascript:void(0);' class='" + Settings.MOVE_UP_SPAN_CLASSES + "'></a>"
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -120,8 +120,9 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
}
|
||||
}
|
||||
|
||||
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername) {
|
||||
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress) {
|
||||
NodePermissions userPerms;
|
||||
|
||||
userPerms.setAll(false);
|
||||
|
||||
if (isLocalUser) {
|
||||
|
@ -136,16 +137,29 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: unverified or no username for" << userPerms.getID() << ", so:" << userPerms;
|
||||
#endif
|
||||
|
||||
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.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 +205,9 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
userPerms.setID(verifiedUsername);
|
||||
userPerms.setVerifiedUserName(verifiedUsername);
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
|
@ -225,7 +242,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->getActiveSocket() : node->getPublicSocket();
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress());
|
||||
}
|
||||
|
||||
node->setPermissions(userPerms);
|
||||
|
@ -337,7 +359,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.",
|
||||
|
@ -430,11 +452,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
|
||||
|
@ -444,8 +466,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) {
|
||||
|
@ -575,10 +597,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";
|
||||
|
|
|
@ -106,7 +106,8 @@ private:
|
|||
QSet<QString> _domainOwnerFriends; // keep track of friends of the domain owner
|
||||
QSet<QString> _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();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -95,6 +95,8 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<Re
|
|||
|
||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||
_argumentList = argumentList;
|
||||
|
||||
// after 1.7 we no longer use the master or merged configs - this is kept in place for migration
|
||||
_configMap.loadMasterAndUserConfig(_argumentList);
|
||||
|
||||
// What settings version were we before and what are we using now?
|
||||
|
@ -118,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)
|
||||
|
@ -129,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);
|
||||
|
||||
|
@ -149,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);
|
||||
|
@ -185,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();
|
||||
|
@ -273,6 +272,28 @@ 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();
|
||||
}
|
||||
|
||||
if (oldVersion < 1.7) {
|
||||
// This was prior to the removal of the master config file
|
||||
// So we write the merged config to the user config file, and stop reading from the user config file
|
||||
|
||||
qDebug() << "Migrating merged config to user config file. The master config file is deprecated.";
|
||||
|
||||
// replace the user config by the merged config
|
||||
_configMap.getConfig() = _configMap.getMergedConfig();
|
||||
|
||||
// persist the new config so the user config file has the correctly merged config
|
||||
persistToFile();
|
||||
}
|
||||
}
|
||||
|
||||
unpackPermissions();
|
||||
|
@ -293,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" };
|
||||
|
@ -318,9 +339,6 @@ void DomainServerSettingsManager::validateDescriptorsMap() {
|
|||
if (wasMalformed) {
|
||||
// write the new settings to file
|
||||
persistToFile();
|
||||
|
||||
// reload the master and user config so the merged config is correct
|
||||
_configMap.loadMasterAndUserConfig(_argumentList);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,16 +372,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 = _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);
|
||||
if (!permissions || !permissions->canConvert(QMetaType::QVariantList)) {
|
||||
permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath, true);
|
||||
QVariant* permissions = _configMap.valueForKeyPath(keyPath, true);
|
||||
if (!permissions->canConvert(QMetaType::QVariantList)) {
|
||||
(*permissions) = QVariantList();
|
||||
}
|
||||
|
||||
|
@ -420,6 +436,9 @@ 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 groups
|
||||
packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH);
|
||||
|
||||
|
@ -427,139 +446,103 @@ void DomainServerSettingsManager::packPermissions() {
|
|||
packPermissionsForMap("permissions", _groupForbiddens, GROUP_FORBIDDENS_KEYPATH);
|
||||
|
||||
persistToFile();
|
||||
_configMap.loadMasterAndUserConfig(_argumentList);
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::unpackPermissions() {
|
||||
// transfer details from _configMap to _agentPermissions;
|
||||
bool DomainServerSettingsManager::unpackPermissionsForKeypath(const QString& keyPath,
|
||||
NodePermissionsMap* mapPointer,
|
||||
std::function<void(NodePermissionsPointer)> 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 = _configMap.valueForKeyPath(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<QVariant> 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<QVariant> 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<QVariant> 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<QVariant> 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 needPack = false;
|
||||
|
||||
needPack |= unpackPermissionsForKeypath(AGENT_STANDARD_PERMISSIONS_KEYPATH, &_standardAgentPermissions);
|
||||
|
||||
needPack |= unpackPermissionsForKeypath(AGENT_PERMISSIONS_KEYPATH, &_agentPermissions);
|
||||
|
||||
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){
|
||||
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) {
|
||||
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();
|
||||
|
@ -572,7 +555,7 @@ void DomainServerSettingsManager::unpackPermissions() {
|
|||
qDebug() << "--------------- permissions ---------------------";
|
||||
QList<QHash<NodePermissionsKey, NodePermissionsPointer>> permissionsSets;
|
||||
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get()
|
||||
<< _groupPermissions.get() << _groupForbiddens.get();
|
||||
<< _groupPermissions.get() << _groupForbiddens.get() << _ipPermissions.get();
|
||||
foreach (auto permissionSet, permissionsSets) {
|
||||
QHashIterator<NodePermissionsKey, NodePermissionsPointer> i(permissionSet);
|
||||
while (i.hasNext()) {
|
||||
|
@ -648,6 +631,89 @@ bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() {
|
|||
return changed;
|
||||
}
|
||||
|
||||
|
||||
void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<ReceivedMessage> 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<LimitedNodeList>();
|
||||
|
||||
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();
|
||||
|
||||
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 {
|
||||
// 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();
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
// make sure we didn't already have existing permissions that disallowed connect
|
||||
if (!hadExistingPermissions
|
||||
|| destinationPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
|
||||
|
||||
qDebug() << "Removing connect permission for node" << uuidStringWithoutCurlyBraces(matchingNode->getUUID())
|
||||
<< "after kick request";
|
||||
|
||||
// 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.";
|
||||
}
|
||||
} 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()) {
|
||||
|
@ -675,6 +741,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)) {
|
||||
|
@ -719,7 +795,7 @@ NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid&
|
|||
}
|
||||
|
||||
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) {
|
||||
const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath);
|
||||
const QVariant* foundValue = _configMap.valueForKeyPath(keyPath);
|
||||
|
||||
if (foundValue) {
|
||||
return *foundValue;
|
||||
|
@ -802,12 +878,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");
|
||||
}
|
||||
|
||||
|
@ -852,13 +926,13 @@ QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& ty
|
|||
QVariant variantValue;
|
||||
|
||||
if (!groupKey.isEmpty()) {
|
||||
QVariant settingsMapGroupValue = _configMap.getMergedConfig().value(groupKey);
|
||||
QVariant settingsMapGroupValue = _configMap.value(groupKey);
|
||||
|
||||
if (!settingsMapGroupValue.isNull()) {
|
||||
variantValue = settingsMapGroupValue.toMap().value(settingName);
|
||||
}
|
||||
} else {
|
||||
variantValue = _configMap.getMergedConfig().value(settingName);
|
||||
variantValue = _configMap.value(settingName);
|
||||
}
|
||||
|
||||
QJsonValue result;
|
||||
|
@ -1000,7 +1074,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
|
||||
|
@ -1112,22 +1186,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<QVariant>* standardPermissionsList = reinterpret_cast<QVariantList*>(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<QVariant>* permissionsList = reinterpret_cast<QVariantList*>(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<QVariant>* permissionsList = reinterpret_cast<QVariantList*>(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<QVariant>* permissionsList = reinterpret_cast<QVariantList*>(forbiddenPermissions);
|
||||
std::sort((*permissionsList).begin(), (*permissionsList).end(), permissionVariantLessThan);
|
||||
|
@ -1147,9 +1221,12 @@ 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.");
|
||||
|
||||
// failed to write, reload whatever the current config state is
|
||||
_configMap.loadConfig(_argumentList);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 IP_PERMISSIONS_KEYPATH = "security.ip_permissions";
|
||||
const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions";
|
||||
const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens";
|
||||
|
||||
|
@ -43,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();
|
||||
|
||||
|
@ -58,6 +58,10 @@ 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 hasPermissionsForIP(const QHostAddress& address) const { return _ipPermissions.contains(address.toString(), 0); }
|
||||
NodePermissions getPermissionsForIP(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);
|
||||
|
@ -100,6 +104,7 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void processNodeKickRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
private:
|
||||
QStringList _argumentList;
|
||||
|
@ -129,11 +134,15 @@ private:
|
|||
void packPermissionsForMap(QString mapName, NodePermissionsMap& permissionsRows, QString keyPath);
|
||||
void packPermissions();
|
||||
void unpackPermissions();
|
||||
bool unpackPermissionsForKeypath(const QString& keyPath, NodePermissionsMap* destinationMapPointer,
|
||||
std::function<void(NodePermissionsPointer)> 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 _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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
@ -258,6 +259,7 @@ signals:
|
|||
void canRezChanged(bool canRez);
|
||||
void canRezTmpChanged(bool canRezTmp);
|
||||
void canWriteAssetsChanged(bool canWriteAssets);
|
||||
void canKickChanged(bool canKick);
|
||||
|
||||
protected slots:
|
||||
void connectedForLocalSocketTest();
|
||||
|
|
|
@ -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<ReceivedMessage> message);
|
||||
void addIgnoredNode(const QUuid& otherNodeID);
|
||||
|
|
|
@ -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.";
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -44,6 +44,7 @@ NodePermissions::NodePermissions(QMap<QString, QVariant> 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<QUuid, GroupRank> groupRanks) {
|
||||
|
@ -63,6 +64,7 @@ QVariant NodePermissions::toVariant(QHash<QUuid, GroupRank> 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();
|
||||
}
|
||||
|
|
|
@ -63,7 +63,8 @@ public:
|
|||
canRezPermanentEntities = 4,
|
||||
canRezTemporaryEntities = 8,
|
||||
canWriteToAssetServer = 16,
|
||||
canConnectPastMaxCapacity = 32
|
||||
canConnectPastMaxCapacity = 32,
|
||||
canKick = 64
|
||||
};
|
||||
Q_DECLARE_FLAGS(Permissions, Permission)
|
||||
Permissions permissions;
|
||||
|
|
|
@ -26,7 +26,7 @@ const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
|||
<< PacketType::NodeJsonStats << PacketType::EntityQuery
|
||||
<< PacketType::OctreeDataNack << PacketType::EntityEditNack
|
||||
<< PacketType::DomainListRequest << PacketType::StopNode
|
||||
<< PacketType::DomainDisconnectRequest;
|
||||
<< PacketType::DomainDisconnectRequest << PacketType::NodeKickRequest;
|
||||
|
||||
const QSet<PacketType> NON_SOURCED_PACKETS = QSet<PacketType>()
|
||||
<< PacketType::StunResponse << PacketType::CreateAssignment << PacketType::RequestAssignment
|
||||
|
|
|
@ -98,7 +98,8 @@ public:
|
|||
NegotiateAudioFormat,
|
||||
SelectedAudioFormat,
|
||||
MoreEntityShapes,
|
||||
LAST_PACKET_TYPE = MoreEntityShapes
|
||||
NodeKickRequest,
|
||||
LAST_PACKET_TYPE = NodeKickRequest
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -13,7 +13,23 @@
|
|||
|
||||
#include <NodeList.h>
|
||||
|
||||
UsersScriptingInterface::UsersScriptingInterface() {
|
||||
// emit a signal when kick permissions have changed
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
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<NodeList>()->ignoreNodeBySessionID(nodeID);
|
||||
}
|
||||
|
||||
void UsersScriptingInterface::kick(const QUuid& nodeID) {
|
||||
// ask the NodeList to kick the user with the given session ID
|
||||
DependencyManager::get<NodeList>()->kickNodeBySessionID(nodeID);
|
||||
}
|
||||
|
||||
bool UsersScriptingInterface::getCanKick() {
|
||||
// ask the NodeList to return our ability to kick
|
||||
return DependencyManager::get<NodeList>()->getThisNodeCanKick();
|
||||
}
|
||||
|
|
|
@ -20,8 +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);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -111,6 +111,13 @@ void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentLi
|
|||
loadMapFromJSONFile(_masterConfig, masterConfigFilepath);
|
||||
}
|
||||
|
||||
// load the user config - that method replace 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() {
|
||||
|
|
|
@ -15,16 +15,22 @@
|
|||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QVariantMap>
|
||||
|
||||
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath, bool shouldCreateIfMissing = false);
|
||||
|
||||
class HifiConfigVariantMap {
|
||||
public:
|
||||
static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList);
|
||||
|
||||
HifiConfigVariantMap();
|
||||
void loadMasterAndUserConfig(const QStringList& argumentList);
|
||||
void loadConfig(const QStringList& argumentList);
|
||||
|
||||
const QVariant value(const QString& key) const { return _userConfig.value(key); }
|
||||
QVariant* valueForKeyPath(const QString& keyPath, bool shouldCreateIfMissing = false)
|
||||
{ return ::valueForKeyPath(_userConfig, keyPath, shouldCreateIfMissing); }
|
||||
|
||||
const QVariantMap& getMasterConfig() const { return _masterConfig; }
|
||||
QVariantMap& getUserConfig() { return _userConfig; }
|
||||
QVariantMap& getMergedConfig() { return _mergedConfig; }
|
||||
QVariantMap& getConfig() { return _userConfig; }
|
||||
|
||||
void mergeMasterAndUserConfigs();
|
||||
|
||||
|
@ -40,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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23.7 34.7" style="enable-background:new 0 0 23.7 34.7;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#414042;}
|
||||
.st1{fill:#58595B;}
|
||||
.st2{fill:#EF3B4E;}
|
||||
</style>
|
||||
<path class="st0" d="M23,12.5c0-6.2-5-11.2-11.2-11.2c-6.2,0-11.2,5-11.2,11.2c0,5.2,3.6,9.6,8.4,10.8l3.2,10.1l2.6-10.2
|
||||
C19.6,21.8,23,17.5,23,12.5z M11.9,22.2c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
|
||||
C21.6,17.9,17.3,22.2,11.9,22.2z"/>
|
||||
<path class="st0" d="M12.3,33.9L9,23.4c-5-1.3-8.5-5.8-8.5-10.9c0-6.2,5.1-11.3,11.3-11.3c6.2,0,11.3,5.1,11.3,11.3
|
||||
c0,5-3.3,9.4-8.1,10.8L12.3,33.9z M11.9,1.4c-6.1,0-11.1,5-11.1,11.1c0,5.1,3.4,9.5,8.3,10.7l0.1,0l0,0.1l3.1,9.7l2.5-9.9l0.1,0
|
||||
c4.7-1.4,8-5.7,8-10.6C22.9,6.4,18,1.4,11.9,1.4z M11.9,22.4c-5.5,0-9.9-4.4-9.9-9.9s4.4-9.9,9.9-9.9s9.9,4.4,9.9,9.9
|
||||
S17.3,22.4,11.9,22.4z M11.9,2.8c-5.3,0-9.7,4.3-9.7,9.7c0,5.3,4.3,9.7,9.7,9.7s9.7-4.3,9.7-9.7C21.5,7.1,17.2,2.8,11.9,2.8z"/>
|
||||
<g>
|
||||
<path class="st0" d="M16,8.3c-0.4-0.4-1.1-0.4-1.5,0L11.8,11L9,8.2c-0.4-0.4-1.1-0.4-1.5,0C7.1,8.7,7,9.4,7.5,9.8l2.7,2.7l-2.7,2.7
|
||||
c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0c0.4-0.4,0.4-1.1,0-1.5l-2.7-2.7l2.7-2.7
|
||||
C16.5,9.4,16.5,8.7,16,8.3z"/>
|
||||
<path class="st1" d="M8.3,17.3c-0.3,0-0.6-0.1-0.9-0.4C7.2,16.7,7,16.4,7,16.1c0-0.3,0.1-0.6,0.4-0.9l2.6-2.7L7.4,9.9
|
||||
c-0.5-0.5-0.5-1.2,0-1.7c0.5-0.5,1.2-0.5,1.7,0l2.7,2.7l2.6-2.7c0.2-0.2,0.5-0.4,0.9-0.4l0,0c0.3,0,0.6,0.1,0.9,0.4l0,0
|
||||
c0,0,0,0,0,0l0,0c0.2,0.2,0.4,0.5,0.4,0.9c0,0.3-0.1,0.6-0.4,0.9l-2.7,2.7l2.7,2.6c0.2,0.2,0.4,0.5,0.4,0.9c0,0.3-0.1,0.6-0.4,0.9
|
||||
c-0.5,0.5-1.3,0.5-1.7,0l-2.7-2.6l-2.7,2.7C8.9,17.2,8.6,17.3,8.3,17.3z M8.3,8.4C8.3,8.4,8.3,8.4,8.3,8.4C8,8.4,7.7,8.3,7.6,8.5
|
||||
C7.4,8.7,7.3,8.9,7.3,9.1c0,0.3,0.1,0.5,0.3,0.6l2.8,2.8l-2.8,2.8c-0.4,0.4-0.4,1,0,1.4c0.4,0.4,1,0.4,1.4,0l2.8-2.8l2.8,2.8
|
||||
c0.4,0.4,1,0.4,1.4,0c0.2-0.2,0.3-0.4,0.3-0.7c0-0.3-0.1-0.5-0.3-0.7l-2.8-2.8L16,9.7c0.2-0.2,0.3-0.4,0.3-0.7
|
||||
c0-0.3-0.1-0.5-0.3-0.7l0,0c-0.2-0.2-0.4-0.3-0.7-0.3l0,0c-0.3,0-0.5,0.1-0.7,0.3l-2.8,2.8L8.9,8.5C8.8,8.3,8.5,8.4,8.3,8.4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M23,12c0-6.2-5-11.2-11.2-11.2C5.7,0.8,0.7,5.9,0.7,12c0,5.2,3.6,9.6,8.4,10.8L12.3,33L15,22.8
|
||||
C19.6,21.4,23,17.1,23,12z M11.9,21.8c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
|
||||
C21.7,17.4,17.3,21.8,11.9,21.8z"/>
|
||||
<path class="st2" d="M16.1,7.8c-0.4-0.4-1.1-0.4-1.5,0l-2.7,2.7L9,7.8c-0.4-0.4-1.1-0.4-1.5,0C7.1,8.3,7.1,9,7.5,9.4l2.7,2.7
|
||||
l-2.7,2.7c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0c0.4-0.4,0.4-1.1,0-1.5l-2.7-2.7
|
||||
l2.7-2.7C16.5,9,16.5,8.3,16.1,7.8z"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.8 KiB |
53
scripts/system/assets/images/ignore-target.svg
Normal file
53
scripts/system/assets/images/ignore-target.svg
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 293 225.4" style="enable-background:new 0 0 293 225.4;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#EF3B4E;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<polygon points="121.5,121.9 152,219 177.7,121.9 "/>
|
||||
<polygon class="st0" points="119.2,119 149.7,216.1 175.4,119 "/>
|
||||
<path d="M16.2,23.2H283c3.9,0,7.1,3.2,7.1,7.1V88c0,3.9-3.2,7.1-7.1,7.1H16.2c-3.9,0-7.1-3.2-7.1-7.1V30.3
|
||||
C9.1,26.4,12.3,23.2,16.2,23.2z"/>
|
||||
<path class="st0" d="M13.9,20.3h266.8c3.9,0,7.1,3.2,7.1,7.1v57.7c0,3.9-3.2,7.1-7.1,7.1H13.9c-3.9,0-7.1-3.2-7.1-7.1V27.4
|
||||
C6.8,23.5,10,20.3,13.9,20.3z"/>
|
||||
<path d="M40,79.8v-41h8v41H40z"/>
|
||||
<path d="M85.6,75.2c-2.8,3.1-6.7,4.9-10.9,4.9c-2.6-0.1-5.2-0.8-7.5-2c-2.1-1.1-4-2.6-5.6-4.3c-1.7-2-3.1-4.3-4-6.7
|
||||
c-1.2-2.6-1.8-5.4-1.9-8.2c0-2.6,0.6-5.3,1.5-7.7c1-2.5,2.5-4.7,4.4-6.6c1.6-1.8,3.6-3.3,5.8-4.4c5.6-2.5,12-2.2,17.4,0.8
|
||||
c2.5,1.6,4.5,3.9,5.8,6.6l-6,4.4c-0.8-1.9-2-3.5-3.7-4.7c-1.7-1.1-3.7-1.6-5.8-1.6c-1.6,0-3.2,0.3-4.6,1.1c-1.4,0.7-2.5,1.8-3.5,3
|
||||
c-1,1.3-1.7,2.8-2.2,4.4c-0.5,1.7-0.8,3.5-0.8,5.3c0,1.8,0.3,3.7,0.9,5.4c0.5,1.6,1.3,3.1,2.4,4.3c1,1.2,2.3,2.2,3.7,2.9
|
||||
c1.4,0.7,3,1.1,4.6,1.1c4-0.2,7.7-2.2,10-5.5v-2.9h-8.3v-5.8h14.8v21h-6.6V75.2z"/>
|
||||
<path d="M108.4,53.5v26.3h-8.3V38.7h6.2l21.4,27V38.8h8v41h-6.2L108.4,53.5z"/>
|
||||
<path d="M163.1,80.1c-2.8-0.1-5.5-0.7-7.9-2c-2.4-1.1-4.5-2.7-6.2-4.7c-1.7-2-3.1-4.2-4-6.7c-0.9-2.5-1.4-5.1-1.3-7.7
|
||||
c0-5.4,2-10.6,5.6-14.6c1.7-1.9,3.8-3.5,6.2-4.6c2.4-1.2,5.1-1.8,7.8-1.7c2.7,0,5.5,0.6,7.9,1.8c2.4,1.2,4.5,2.8,6.2,4.8
|
||||
c3.4,4,5.3,9.1,5.3,14.4c0,2.7-0.5,5.3-1.4,7.8c-0.9,2.4-2.3,4.6-4,6.6c-1.7,1.9-3.8,3.4-6.2,4.5C168.5,79.3,165.8,80,163.1,80.1z
|
||||
M151.7,59.3c0,1.7,0.3,3.5,0.8,5.1c0.5,1.6,1.2,3.1,2.2,4.4c1,1.3,2.2,2.3,3.7,3.1c1.5,0.8,3.1,1.2,4.8,1.2
|
||||
c1.7,0.1,3.4-0.3,4.9-1.2c1.4-0.9,2.7-2.1,3.6-3.5c1-1.3,1.7-2.8,2.2-4.4c0.5-1.6,0.8-3.3,0.8-5c0-1.7-0.2-3.5-0.8-5.1
|
||||
c-0.5-1.6-1.3-3.1-2.3-4.4c-1.1-1.2-2.5-2.1-4-2.7c-3-1.5-6.6-1.5-9.6,0c-1.4,0.8-2.6,1.8-3.6,3.1c-1,1.3-1.7,2.8-2.2,4.4
|
||||
C151.8,55.9,151.6,57.6,151.7,59.3z"/>
|
||||
<path d="M190.3,79.8V38.7h18.1c1.8,0,3.6,0.4,5.2,1.2c1.6,0.8,3,1.8,4.1,3.1c1.2,1.3,2.1,2.8,2.7,4.4c0.6,1.6,1,3.2,1,4.9
|
||||
c0,2.6-0.7,5.1-2,7.2c-1.2,2.1-3.1,3.8-5.4,4.7l9.6,15.4h-8.9L206.2,66h-7.9v13.8H190.3z M198.3,58.8h9.6c0.8,0.2,1.6,0.2,2.4,0
|
||||
c0.6-0.4,1.2-0.9,1.6-1.4c0.5-0.6,0.8-1.4,1.1-2.1c0.1-0.9,0.1-1.7,0-2.6c0.2-0.9,0.2-1.8,0-2.7c-0.3-0.8-0.7-1.5-1.3-2.1
|
||||
c-0.5-0.6-1.1-1-1.8-1.3c-0.6-0.3-1.3-0.5-2-0.5h-9.6L198.3,58.8z"/>
|
||||
<path d="M259.2,72.8v6.9h-28.9V38.7h28v7h-19.6v9.6h17.3v6.7h-17.3v10.7H259.2z"/>
|
||||
<path class="st1" d="M38.1,78.1V37.2h8v40.9H38.1z"/>
|
||||
<path class="st1" d="M83.7,73.5c-2.9,3-6.9,4.6-11,4.5c-2.5,0.1-5-0.3-7.3-1.3c-2.3-1.1-4.4-2.7-6.1-4.6c-1.7-2-3.1-4.3-4-6.7
|
||||
c-1-2.5-1.5-5.2-1.4-7.9c-0.1-2.8,0.4-5.6,1.4-8.3c1-2.4,2.4-4.7,4.1-6.6c1.8-1.9,3.8-3.4,6.2-4.4c5.6-2.3,12-1.8,17.3,1.3
|
||||
c2.6,1.6,4.7,3.8,6,6.6l-6,4.4c-0.9-1.9-2.3-3.6-4.1-4.7c-1.7-1.1-3.7-1.6-5.8-1.6c-1.6,0-3.2,0.3-4.6,1.1c-1.4,0.7-2.5,1.8-3.5,3
|
||||
c-1,1.3-1.7,2.8-2.2,4.4c-0.6,1.6-1,3.3-1.1,5c0,1.8,0.3,3.7,0.9,5.4c0.5,1.6,1.3,3.1,2.4,4.3c1,1.2,2.3,2.2,3.7,2.9
|
||||
c1.4,0.7,3,1.1,4.6,1.1c4.2,0,8.2-2.1,10.6-5.5v-2.9h-8.3V57h15v21h-6.7V73.5z"/>
|
||||
<path class="st1" d="M106.6,51.8v26.2h-8v-41h6.2l21.4,27V37.2h8v40.9h-6.5L106.6,51.8z"/>
|
||||
<path class="st1" d="M161.2,78.1c-2.7,0-5.4-0.6-7.9-1.7c-2.4-1.1-4.5-2.7-6.2-4.7c-1.7-2-3.1-4.2-4-6.7c-0.9-2.5-1.4-5.1-1.3-7.7
|
||||
c0-5.4,2-10.6,5.6-14.6c1.7-1.9,3.8-3.5,6.2-4.6c2.4-1.2,5.1-1.8,7.8-1.7c2.7,0,5.5,0.6,7.9,1.8c2.4,1.2,4.5,2.8,6.2,4.8
|
||||
c3.4,4,5.3,9.1,5.3,14.4c0,2.7-0.5,5.3-1.4,7.8c-0.9,2.4-2.3,4.6-4,6.6c-1.7,1.9-3.8,3.4-6.2,4.5C166.7,77.4,164,78.1,161.2,78.1z
|
||||
M149.9,57.6c0,1.7,0.3,3.5,0.8,5.1c0.5,1.6,1.2,3.1,2.2,4.4c1,1.3,2.2,2.3,3.7,3.1c1.5,0.8,3.1,1.2,4.8,1.2
|
||||
c1.7,0.1,3.4-0.3,4.9-1.2c1.4-0.8,2.6-1.9,3.6-3.2c1-1.3,1.7-2.8,2.2-4.4c0.5-1.6,0.8-3.3,0.8-5c0-1.7-0.2-3.5-0.8-5.1
|
||||
c-0.5-1.6-1.3-3.1-2.3-4.4c-1-1.2-2.2-2.3-3.6-3c-3-1.5-6.6-1.5-9.6,0c-1.4,0.8-2.6,1.8-3.6,3.1c-1,1.3-1.7,2.8-2.2,4.4
|
||||
C150.2,54.2,149.9,55.9,149.9,57.6z"/>
|
||||
<path class="st1" d="M188.4,78.1v-41h17.7c1.8,0,3.6,0.4,5.2,1.2c1.7,0.7,3.2,1.8,4.4,3.2c1.2,1.3,2.1,2.8,2.7,4.4
|
||||
c0.6,1.6,1,3.2,1,4.9c0,2.6-0.7,5.1-2,7.2c-1.2,2.1-3.1,3.8-5.4,4.7l9.6,15.4h-8.8l-8.6-13.8h-7.8v13.7H188.4z M196.5,57.4h9.6
|
||||
c0.7,0,1.4-0.2,2-0.5c0.6-0.4,1.2-0.9,1.6-1.4c0.5-0.6,0.8-1.4,1.1-2.1c0.1-0.9,0.1-1.7,0-2.6c0.2-0.9,0.2-1.8,0-2.7
|
||||
c-0.3-0.8-0.7-1.5-1.3-2.1c-0.5-0.6-1.1-1-1.8-1.3c-0.5-0.3-1.1-0.4-1.6-0.5h-9.6V57.4z"/>
|
||||
<path class="st1" d="M257.4,71.1v6.9h-28.9v-41h28v7h-19.6v9.6h17.5v6.7h-17.5v10.7H257.4z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 4.6 KiB |
33
scripts/system/assets/images/kick-target.svg
Normal file
33
scripts/system/assets/images/kick-target.svg
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 293 225.4" style="enable-background:new 0 0 293 225.4;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#EF3B4E;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path d="M15.1,22.7h266.8c3.9,0,7.1,3.2,7.1,7.1v57.7c0,3.9-3.2,7.1-7.1,7.1H15.1c-3.9,0-7.1-3.2-7.1-7.1V29.9
|
||||
C7.9,25.9,11.1,22.7,15.1,22.7z"/>
|
||||
<polygon points="120.4,121.9 150.8,219 176.6,121.9 "/>
|
||||
<path class="st0" d="M12.8,19.9h266.8c3.9,0,7.1,3.2,7.1,7.1v57.7c0,3.9-3.2,7.1-7.1,7.1H12.8c-3.9,0-7.1-3.2-7.1-7.1V27
|
||||
C5.6,23,8.8,19.9,12.8,19.9z"/>
|
||||
<path d="M81.8,74.3V34.8h7.5v19.3l17.2-19.3h8L99,52.3l16.1,22h-7.7L94.8,56.7l-5.5,5.7v12H81.8z"/>
|
||||
<path d="M120.4,74.3V34.8h7.5v39.5h-7.7H120.4z"/>
|
||||
<path d="M135.4,54.3c0-2.4,0.4-4.8,1.3-7.1c0.8-2.3,2.1-4.5,3.7-6.4c1.7-1.9,3.7-3.4,6-4.5c2.5-1.2,5.2-1.8,8-1.7
|
||||
c3.4-0.2,6.7,0.7,9.6,2.4c2.5,1.5,4.4,3.7,5.7,6.3l-5.9,4c-0.4-1-1-2-1.7-2.8c-0.7-0.7-1.4-1.4-2.3-1.8c-0.8-0.4-1.7-0.8-2.6-1
|
||||
c-0.9-0.1-1.7-0.1-2.6,0c-1.7,0-3.3,0.4-4.8,1.2c-1.4,0.8-2.6,1.8-3.5,3.1c-0.9,1.3-1.6,2.7-2,4.2c-0.5,1.5-0.7,3.1-0.7,4.7
|
||||
c0,1.7,0.2,3.4,0.8,5c0.5,1.5,1.3,3,2.3,4.2c1,1.2,2.2,2.2,3.6,3c1.4,0.8,2.9,1.1,4.5,1.2c0.7,0,1.4-0.1,2.1-0.3
|
||||
c1.9-0.4,3.6-1.5,4.9-2.9c0.7-0.8,1.3-1.8,1.7-2.8l6.3,3.7c-0.6,1.5-1.5,2.8-2.6,4c-1.1,1.1-2.4,2.1-3.9,2.9c-1.4,0.8-3,1.4-4.5,1.8
|
||||
c-1.5,0.4-3.1,0.6-4.7,0.6c-2.6,0-5.2-0.6-7.5-1.8c-2.3-1.4-4.3-3.2-5.9-5.4C137.3,64.1,135.5,59.3,135.4,54.3z"/>
|
||||
<path d="M176.1,74.3V34.8h7.7v19.3L201,34.8h8l-15.1,17.5l16.1,22H202l-12.6-17.6l-5.3,5.8v12L176.1,74.3L176.1,74.3z"/>
|
||||
<path class="st1" d="M79.7,72.7V33.2h7.7v19.3l17.2-19.3h8L97.6,50.6l16.1,22h-8.1L93,54.9l-5.3,5.8v12H79.7z"/>
|
||||
<path class="st1" d="M118.2,72.7V33.2h7.7v39.5H118.2z"/>
|
||||
<path class="st1" d="M133.6,52.5c0-2.4,0.4-4.8,1.3-7.1c0.8-2.3,2.1-4.5,3.8-6.4c1.7-1.9,3.7-3.4,6-4.5c2.5-1.2,5.2-1.8,8-1.7
|
||||
c3.4-0.2,6.7,0.7,9.6,2.4c2.5,1.5,4.4,3.7,5.7,6.3l-5.9,4c-0.4-1-1-2-1.7-2.8c-0.7-0.7-1.4-1.4-2.3-1.8c-0.8-0.4-1.7-0.8-2.6-1
|
||||
c-0.9-0.1-1.7-0.1-2.6,0c-1.7,0-3.3,0.4-4.8,1.2c-1.4,0.8-2.6,1.8-3.5,3.1c-0.9,1.3-1.6,2.7-2,4.2c-0.5,1.5-0.7,3.1-0.7,4.7
|
||||
c0,1.7,0.2,3.4,0.8,5c0.5,1.5,1.3,3,2.3,4.2c1,1.2,2.2,2.2,3.6,3c1.4,0.8,2.9,1.1,4.5,1.2c0.9,0.1,1.7,0.1,2.6,0
|
||||
c1.9-0.4,3.6-1.5,4.9-2.9c0.7-0.8,1.3-1.8,1.7-2.8l6.3,3.7c-0.6,1.5-1.5,2.8-2.6,4c-1.1,1.1-2.4,2.1-3.9,2.9c-1.4,0.8-3,1.4-4.5,1.8
|
||||
c-1.5,0.4-3.1,0.6-4.7,0.6c-2.6,0-5.2-0.6-7.5-1.8c-2.2-1.2-4.2-2.7-5.9-4.6C135.6,63.3,133.6,58,133.6,52.5z"/>
|
||||
<path class="st1" d="M174.6,72.7V33.2h7.7v19.3l17.2-19.3h8l-15.2,17.5l16.1,22h-8.1l-12.6-17.6l-5.3,5.8v12H174.6z"/>
|
||||
<polygon class="st0" points="118,119 148.5,216.1 174.3,119 "/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
140
scripts/system/assets/images/tools/kick.svg
Normal file
140
scripts/system/assets/images/tools/kick.svg
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 200.1" style="enable-background:new 0 0 50 200.1;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#414042;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
.st2{fill:#1E1E1E;}
|
||||
.st3{fill:#333333;}
|
||||
</style>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M50.1,146.1c0,2.2-1.8,4-4,4h-42c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V146.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M50,196.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V196.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M50,46c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4V4c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V46z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M50,96.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V96.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path class="st1" d="M24.7,81.2c-6.2,0-11.2-5-11.2-11.2c0-6.2,5-11.2,11.2-11.2c6.2,0,11.2,5,11.2,11.2
|
||||
C35.9,76.2,30.9,81.2,24.7,81.2z M24.7,60.3c-5.4,0-9.8,4.4-9.8,9.8c0,5.4,4.4,9.8,9.8,9.8c5.4,0,9.8-4.4,9.8-9.8
|
||||
C34.5,64.6,30.1,60.3,24.7,60.3z"/>
|
||||
<path class="st1" d="M27.1,79.5c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
|
||||
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9c-0.3-5.9-5.2-10.6-11.2-10.6c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
|
||||
c1,0,2-0.2,3-0.4C27.5,80.4,27.3,80,27.1,79.5z"/>
|
||||
<path class="st1" d="M31.2,66.5L31.2,66.5c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
|
||||
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
|
||||
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
|
||||
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
|
||||
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
|
||||
c0.3-0.1,0.4-0.3,0.5-0.6C31.4,66.9,31.4,66.7,31.2,66.5z"/>
|
||||
<circle class="st1" cx="24.7" cy="64" r="1.7"/>
|
||||
<path class="st3" d="M27.1,29.7c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
|
||||
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9C35.6,13.7,30.7,9,24.7,9c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
|
||||
c1,0,2-0.2,3-0.4C27.5,30.5,27.3,30.1,27.1,29.7z"/>
|
||||
<path class="st3" d="M31.2,16.6L31.2,16.6c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
|
||||
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
|
||||
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
|
||||
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
|
||||
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
|
||||
c0.3-0.1,0.4-0.3,0.5-0.6C31.4,17.1,31.4,16.8,31.2,16.6z"/>
|
||||
<circle class="st3" cx="24.7" cy="14.1" r="1.7"/>
|
||||
<g>
|
||||
<polygon class="st3" points="36.4,30.1 36.4,30.1 36.4,30.1 "/>
|
||||
<path class="st3" d="M35.2,25.8l2.7-2.7c0.4-0.4,0.4-1.1,0-1.5c-0.4-0.4-1.1-0.4-1.5,0l-2.7,2.7l-2.7-2.7c-0.4-0.4-1.1-0.4-1.5,0
|
||||
c-0.4,0.4-0.4,1.1,0,1.5l2.7,2.7l-2.7,2.7c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0
|
||||
c0.4-0.4,0.4-1.1,0-1.5L35.2,25.8z"/>
|
||||
</g>
|
||||
<path class="st1" d="M27.3,129.6c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
|
||||
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9c-0.3-5.9-5.2-10.6-11.2-10.6c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
|
||||
c1,0,2-0.2,3-0.4C27.6,130.5,27.4,130.1,27.3,129.6z"/>
|
||||
<path class="st1" d="M31.3,116.6L31.3,116.6c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
|
||||
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
|
||||
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
|
||||
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
|
||||
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
|
||||
c0.3-0.1,0.4-0.3,0.5-0.6C31.6,117,31.5,116.8,31.3,116.6z"/>
|
||||
<circle class="st1" cx="24.9" cy="114.1" r="1.7"/>
|
||||
<g>
|
||||
<polygon class="st1" points="36.5,130.1 36.5,130.1 36.5,130.1 "/>
|
||||
<path class="st1" d="M35.3,125.8l2.7-2.7c0.4-0.4,0.4-1.1,0-1.5c-0.4-0.4-1.1-0.4-1.5,0l-2.7,2.7l-2.7-2.7c-0.4-0.4-1.1-0.4-1.5,0
|
||||
c-0.4,0.4-0.4,1.1,0,1.5l2.7,2.7l-2.7,2.7c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0
|
||||
c0.4-0.4,0.4-1.1,0-1.5L35.3,125.8z"/>
|
||||
</g>
|
||||
<path class="st1" d="M27.1,179.9c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
|
||||
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9c-0.3-5.9-5.2-10.6-11.2-10.6c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
|
||||
c1,0,2-0.2,3-0.4C27.5,180.7,27.3,180.3,27.1,179.9z"/>
|
||||
<path class="st1" d="M31.2,166.8L31.2,166.8c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
|
||||
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
|
||||
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
|
||||
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
|
||||
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
|
||||
c0.3-0.1,0.4-0.3,0.5-0.6C31.4,167.3,31.4,167,31.2,166.8z"/>
|
||||
<circle class="st1" cx="24.7" cy="164.3" r="1.7"/>
|
||||
<g>
|
||||
<polygon class="st1" points="36.4,180.3 36.4,180.3 36.4,180.3 "/>
|
||||
<path class="st1" d="M35.2,176l2.7-2.7c0.4-0.4,0.4-1.1,0-1.5c-0.4-0.4-1.1-0.4-1.5,0l-2.7,2.7l-2.7-2.7c-0.4-0.4-1.1-0.4-1.5,0
|
||||
c-0.4,0.4-0.4,1.1,0,1.5l2.7,2.7l-2.7,2.7c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0
|
||||
c0.4-0.4,0.4-1.1,0-1.5L35.2,176z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M14.7,92.3v-6.4h1.2v3.1l2.8-3.1H20l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H14.7z"/>
|
||||
<path class="st1" d="M20.9,92.3v-6.4h1.2v6.4H20.9z"/>
|
||||
<path class="st1" d="M23.3,89.1c0-0.4,0.1-0.8,0.2-1.2c0.1-0.4,0.3-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.3-0.3
|
||||
c0.6,0,1.1,0.1,1.5,0.4c0.4,0.3,0.7,0.6,0.9,1l-1,0.7c-0.1-0.2-0.2-0.3-0.3-0.5c-0.1-0.1-0.2-0.2-0.4-0.3c-0.1-0.1-0.3-0.1-0.4-0.2
|
||||
c-0.1,0-0.3,0-0.4,0c-0.3,0-0.6,0.1-0.8,0.2s-0.4,0.3-0.6,0.5c-0.1,0.2-0.3,0.4-0.3,0.7s-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8
|
||||
c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.1,0,0.3,0,0.4-0.1c0.1,0,0.3-0.1,0.4-0.2
|
||||
c0.1-0.1,0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4l1,0.6c-0.1,0.2-0.2,0.5-0.4,0.6c-0.2,0.2-0.4,0.3-0.6,0.5
|
||||
c-0.2,0.1-0.5,0.2-0.7,0.3c-0.3,0.1-0.5,0.1-0.8,0.1c-0.4,0-0.9-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-1-0.8c-0.3-0.3-0.5-0.7-0.6-1.1
|
||||
C23.4,89.9,23.3,89.5,23.3,89.1z"/>
|
||||
<path class="st1" d="M30,92.3v-6.4h1.2v3.1l2.8-3.1h1.3l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H30z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M14.7,42.6v-6.4h1.2v3.1l2.8-3.1H20l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H14.7z"/>
|
||||
<path class="st3" d="M20.9,42.6v-6.4h1.2v6.4H20.9z"/>
|
||||
<path class="st3" d="M23.3,39.4c0-0.4,0.1-0.8,0.2-1.2c0.1-0.4,0.3-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.3-0.3
|
||||
c0.6,0,1.1,0.1,1.5,0.4c0.4,0.3,0.7,0.6,0.9,1l-1,0.7c-0.1-0.2-0.2-0.3-0.3-0.5c-0.1-0.1-0.2-0.2-0.4-0.3c-0.1-0.1-0.3-0.1-0.4-0.2
|
||||
c-0.1,0-0.3,0-0.4,0c-0.3,0-0.6,0.1-0.8,0.2S25.2,37.8,25,38c-0.1,0.2-0.3,0.4-0.3,0.7s-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8
|
||||
c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.1,0,0.3,0,0.4-0.1c0.1,0,0.3-0.1,0.4-0.2
|
||||
c0.1-0.1,0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4l1,0.6c-0.1,0.2-0.2,0.5-0.4,0.6c-0.2,0.2-0.4,0.3-0.6,0.5
|
||||
c-0.2,0.1-0.5,0.2-0.7,0.3c-0.3,0.1-0.5,0.1-0.8,0.1c-0.4,0-0.9-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-1-0.8c-0.3-0.3-0.5-0.7-0.6-1.1
|
||||
C23.4,40.2,23.3,39.8,23.3,39.4z"/>
|
||||
<path class="st3" d="M30,42.6v-6.4h1.2v3.1l2.8-3.1h1.3l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H30z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M14.8,142.4v-6.4H16v3.1l2.8-3.1h1.3l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H14.8z"/>
|
||||
<path class="st1" d="M21,142.4v-6.4h1.2v6.4H21z"/>
|
||||
<path class="st1" d="M23.4,139.2c0-0.4,0.1-0.8,0.2-1.2c0.1-0.4,0.3-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7c0.4-0.2,0.8-0.3,1.3-0.3
|
||||
c0.6,0,1.1,0.1,1.5,0.4c0.4,0.3,0.7,0.6,0.9,1l-1,0.7c-0.1-0.2-0.2-0.3-0.3-0.5c-0.1-0.1-0.2-0.2-0.4-0.3c-0.1-0.1-0.3-0.1-0.4-0.2
|
||||
c-0.1,0-0.3,0-0.4,0c-0.3,0-0.6,0.1-0.8,0.2s-0.4,0.3-0.6,0.5c-0.1,0.2-0.3,0.4-0.3,0.7s-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8
|
||||
c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.1,0,0.3,0,0.4-0.1c0.1,0,0.3-0.1,0.4-0.2
|
||||
s0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4l1,0.6c-0.1,0.2-0.2,0.5-0.4,0.6c-0.2,0.2-0.4,0.3-0.6,0.5c-0.2,0.1-0.5,0.2-0.7,0.3
|
||||
c-0.3,0.1-0.5,0.1-0.8,0.1c-0.4,0-0.9-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-1-0.8c-0.3-0.3-0.5-0.7-0.6-1.1
|
||||
C23.5,140,23.4,139.6,23.4,139.2z"/>
|
||||
<path class="st1" d="M30.1,142.4v-6.4h1.2v3.1l2.8-3.1h1.3l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H30.1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M14.7,192.7v-6.4h1.2v3.1l2.8-3.1H20l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H14.7z"/>
|
||||
<path class="st1" d="M20.9,192.7v-6.4h1.2v6.4H20.9z"/>
|
||||
<path class="st1" d="M23.3,189.4c0-0.4,0.1-0.8,0.2-1.2c0.1-0.4,0.3-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7c0.4-0.2,0.8-0.3,1.3-0.3
|
||||
c0.6,0,1.1,0.1,1.5,0.4s0.7,0.6,0.9,1l-1,0.7c-0.1-0.2-0.2-0.3-0.3-0.5c-0.1-0.1-0.2-0.2-0.4-0.3s-0.3-0.1-0.4-0.2
|
||||
c-0.1,0-0.3,0-0.4,0c-0.3,0-0.6,0.1-0.8,0.2s-0.4,0.3-0.6,0.5c-0.1,0.2-0.3,0.4-0.3,0.7s-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8
|
||||
c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.1,0,0.3,0,0.4-0.1c0.1,0,0.3-0.1,0.4-0.2
|
||||
c0.1-0.1,0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4l1,0.6c-0.1,0.2-0.2,0.5-0.4,0.6c-0.2,0.2-0.4,0.3-0.6,0.5
|
||||
c-0.2,0.1-0.5,0.2-0.7,0.3c-0.3,0.1-0.5,0.1-0.8,0.1c-0.4,0-0.9-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-1-0.8c-0.3-0.3-0.5-0.7-0.6-1.1
|
||||
C23.4,190.2,23.3,189.8,23.3,189.4z"/>
|
||||
<path class="st1" d="M29.9,192.7v-6.4h1.2v3.1l2.8-3.1h1.3l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H29.9z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 10 KiB |
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// ignore.js
|
||||
// mod.js
|
||||
// scripts/system/
|
||||
//
|
||||
// Created by Stephen Birarda on 07/11/2016
|
||||
|
@ -12,10 +12,14 @@
|
|||
// grab the toolbar
|
||||
var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
|
||||
|
||||
// setup the ignore button and add it to the toolbar
|
||||
function buttonImageURL() {
|
||||
return Script.resolvePath("assets/images/tools/" + (Users.canKick ? 'kick.svg' : 'ignore.svg'));
|
||||
}
|
||||
|
||||
// setup the mod button and add it to the toolbar
|
||||
var button = toolbar.addButton({
|
||||
objectName: 'ignore',
|
||||
imageURL: Script.resolvePath("assets/images/tools/ignore.svg"),
|
||||
objectName: 'mod',
|
||||
imageURL: buttonImageURL(),
|
||||
visible: true,
|
||||
buttonState: 1,
|
||||
defaultState: 2,
|
||||
|
@ -23,19 +27,24 @@ 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 = {};
|
||||
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
|
||||
|
@ -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) {
|
||||
|
||||
|
@ -80,17 +93,18 @@ 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], {
|
||||
position: overlayPosition
|
||||
Overlays.editOverlay(modOverlays[avatarID], {
|
||||
position: overlayPosition,
|
||||
url: overlayURL()
|
||||
});
|
||||
} 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,
|
||||
size: 1,
|
||||
scale: 0.4,
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
|
@ -100,7 +114,7 @@ function updateOverlays() {
|
|||
});
|
||||
|
||||
// push this overlay to our array of overlays
|
||||
ignoreOverlays[avatarID] = newOverlay;
|
||||
modOverlays[avatarID] = newOverlay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,24 +127,28 @@ 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
|
||||
Users.ignore(avatarID);
|
||||
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 {
|
||||
Users.ignore(avatarID);
|
||||
}
|
||||
|
||||
// cleanup of the overlay is handled by the connection to avatarRemovedEvent
|
||||
}
|
||||
|
@ -161,15 +179,9 @@ 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);
|
||||
if (controllerPose.valid && triggered) {
|
||||
function controllerComputePickRay(hand) {
|
||||
var controllerPose = Controller.getPoseValue(hand);
|
||||
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:
|
||||
|
@ -178,37 +190,27 @@ function controllerComputePickRay() {
|
|||
}
|
||||
}
|
||||
|
||||
function makeTriggerHandler(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;
|
||||
function makeClickHandler(hand) {
|
||||
return function(clicked) {
|
||||
if (clicked == 1.0 && isShowingOverlays) {
|
||||
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();
|
||||
|
||||
// cleanup the toolbar button and overlays when script is stopped
|
||||
Script.scriptEnding.connect(function() {
|
||||
toolbar.removeButton('ignore');
|
||||
toolbar.removeButton('mod');
|
||||
removeOverlays();
|
||||
triggerMapping.disable();
|
||||
});
|
Loading…
Reference in a new issue