mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-26 02:55:25 +02:00
Merge pull request #12429 from birarda/feat/pending-content-archives
availability and restore progress for content archives, move perm defaults, add settings manager thread safety
This commit is contained in:
commit
42afdd6502
10 changed files with 331 additions and 227 deletions
|
@ -306,7 +306,37 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"non-deletable-row-key": "permissions_id",
|
"non-deletable-row-key": "permissions_id",
|
||||||
"non-deletable-row-values": [ "localhost", "anonymous", "logged-in" ]
|
"non-deletable-row-values": [ "localhost", "anonymous", "logged-in" ],
|
||||||
|
"default": [
|
||||||
|
{
|
||||||
|
"id_can_connect": true,
|
||||||
|
"id_can_rez_tmp_certified": true,
|
||||||
|
"permissions_id": "anonymous"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id_can_connect": true,
|
||||||
|
"id_can_rez_tmp_certified": true,
|
||||||
|
"permissions_id": "friends"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id_can_adjust_locks": true,
|
||||||
|
"id_can_connect": true,
|
||||||
|
"id_can_connect_past_max_capacity": true,
|
||||||
|
"id_can_kick": true,
|
||||||
|
"id_can_replace_content": true,
|
||||||
|
"id_can_rez": true,
|
||||||
|
"id_can_rez_certified": true,
|
||||||
|
"id_can_rez_tmp": true,
|
||||||
|
"id_can_rez_tmp_certified": true,
|
||||||
|
"id_can_write_to_asset_server": true,
|
||||||
|
"permissions_id": "localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id_can_connect": true,
|
||||||
|
"id_can_rez_tmp_certified": true,
|
||||||
|
"permissions_id": "logged-in"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "group_permissions",
|
"name": "group_permissions",
|
||||||
|
|
|
@ -2,10 +2,19 @@ $(document).ready(function(){
|
||||||
|
|
||||||
var RESTORE_SETTINGS_UPLOAD_ID = 'restore-settings-button';
|
var RESTORE_SETTINGS_UPLOAD_ID = 'restore-settings-button';
|
||||||
var RESTORE_SETTINGS_FILE_ID = 'restore-settings-file';
|
var RESTORE_SETTINGS_FILE_ID = 'restore-settings-file';
|
||||||
|
var UPLOAD_CONTENT_ALLOWED_DIV_ID = 'upload-content-allowed';
|
||||||
|
var UPLOAD_CONTENT_RECOVERING_DIV_ID = 'upload-content-recovering';
|
||||||
|
|
||||||
|
function progressBarHTML(extraClass, label) {
|
||||||
|
var html = "<div class='progress'>";
|
||||||
|
html += "<div class='" + extraClass + " progress-bar progress-bar-success progress-bar-striped active' role='progressbar' aria-valuemin='0' aria-valuemax='100'>";
|
||||||
|
html += label + "<span class='sr-only'></span></div></div>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
function setupBackupUpload() {
|
function setupBackupUpload() {
|
||||||
// construct the HTML needed for the settings backup panel
|
// construct the HTML needed for the settings backup panel
|
||||||
var html = "<div class='form-group'>";
|
var html = "<div class='form-group'><div id='" + UPLOAD_CONTENT_ALLOWED_DIV_ID + "'>";
|
||||||
|
|
||||||
html += "<span class='help-block'>Upload a content archive (.zip) or entity file (.json, .json.gz) to replace the content of this domain.";
|
html += "<span class='help-block'>Upload a content archive (.zip) or entity file (.json, .json.gz) to replace the content of this domain.";
|
||||||
html += "<br/>Note: Your domain content will be replaced by the content you upload, but the existing backup files of your domain's content will not immediately be changed.</span>";
|
html += "<br/>Note: Your domain content will be replaced by the content you upload, but the existing backup files of your domain's content will not immediately be changed.</span>";
|
||||||
|
@ -13,7 +22,10 @@ $(document).ready(function(){
|
||||||
html += "<input id='restore-settings-file' name='restore-settings' type='file'>";
|
html += "<input id='restore-settings-file' name='restore-settings' type='file'>";
|
||||||
html += "<button type='button' id='" + RESTORE_SETTINGS_UPLOAD_ID + "' disabled='true' class='btn btn-primary'>Upload Content</button>";
|
html += "<button type='button' id='" + RESTORE_SETTINGS_UPLOAD_ID + "' disabled='true' class='btn btn-primary'>Upload Content</button>";
|
||||||
|
|
||||||
html += "</div>";
|
html += "</div><div id='" + UPLOAD_CONTENT_RECOVERING_DIV_ID + "'>";
|
||||||
|
html += "<span class='help-block'>Restore in progress</span>";
|
||||||
|
html += progressBarHTML('recovery', 'Restoring');
|
||||||
|
html += "</div></div>";
|
||||||
|
|
||||||
$('#' + Settings.UPLOAD_CONTENT_BACKUP_PANEL_ID + ' .panel-body').html(html);
|
$('#' + Settings.UPLOAD_CONTENT_BACKUP_PANEL_ID + ' .panel-body').html(html);
|
||||||
}
|
}
|
||||||
|
@ -66,25 +78,30 @@ $(document).ready(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
var GENERATE_ARCHIVE_BUTTON_ID = 'generate-archive-button';
|
var GENERATE_ARCHIVE_BUTTON_ID = 'generate-archive-button';
|
||||||
|
var CONTENT_ARCHIVES_NORMAL_ID = 'content-archives-success';
|
||||||
|
var CONTENT_ARCHIVES_ERROR_ID = 'content-archives-error';
|
||||||
var AUTOMATIC_ARCHIVES_TABLE_ID = 'automatic-archives-table';
|
var AUTOMATIC_ARCHIVES_TABLE_ID = 'automatic-archives-table';
|
||||||
var AUTOMATIC_ARCHIVES_TBODY_ID = 'automatic-archives-tbody';
|
var AUTOMATIC_ARCHIVES_TBODY_ID = 'automatic-archives-tbody';
|
||||||
var MANUAL_ARCHIVES_TABLE_ID = 'manual-archives-table';
|
var MANUAL_ARCHIVES_TABLE_ID = 'manual-archives-table';
|
||||||
var MANUAL_ARCHIVES_TBODY_ID = 'manual-archives-tbody';
|
var MANUAL_ARCHIVES_TBODY_ID = 'manual-archives-tbody';
|
||||||
var AUTO_ARCHIVES_SETTINGS_LINK_ID = 'auto-archives-settings-link';
|
var AUTO_ARCHIVES_SETTINGS_LINK_ID = 'auto-archives-settings-link';
|
||||||
|
var ACTION_MENU_CLASS = 'action-menu';
|
||||||
|
|
||||||
var automaticBackups = [];
|
var automaticBackups = [];
|
||||||
var manualBackups = [];
|
var manualBackups = [];
|
||||||
|
|
||||||
function setupContentArchives() {
|
function setupContentArchives() {
|
||||||
// construct the HTML needed for the content archives panel
|
// construct the HTML needed for the content archives panel
|
||||||
var html = "<div class='form-group'>";
|
var html = "<div id='" + CONTENT_ARCHIVES_NORMAL_ID + "'><div class='form-group'>";
|
||||||
html += "<label class='control-label'>Automatic Content Archives</label>";
|
html += "<label class='control-label'>Automatic Content Archives</label>";
|
||||||
html += "<span class='help-block'>Your domain server makes regular archives of the content in your domain. In the list below, you can see and download all of your domain content and settings backups. "
|
html += "<span class='help-block'>Your domain server makes regular archives of the content in your domain. In the list below, you can see and download all of your domain content and settings backups. "
|
||||||
html += "<a href='/settings/#automatic_content_archives' id='" + AUTO_ARCHIVES_SETTINGS_LINK_ID + "'>Click here to manage automatic content archive intervals.</a>";
|
html += "<a href='/settings/#automatic_content_archives' id='" + AUTO_ARCHIVES_SETTINGS_LINK_ID + "'>Click here to manage automatic content archive intervals.</a></span>";
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
html += "<table class='table sortable' id='" + AUTOMATIC_ARCHIVES_TABLE_ID + "'>";
|
html += "<table class='table sortable' id='" + AUTOMATIC_ARCHIVES_TABLE_ID + "'>";
|
||||||
|
|
||||||
var backups_table_head = "<thead><tr class='gray-tr'><th>Archive Name</th><th data-defaultsort='desc'>Archive Date</th><th class='text-right' data-defaultsort='disabled'>Actions</th></tr></thead>";
|
var backups_table_head = "<thead><tr class='gray-tr'><th>Archive Name</th><th data-defaultsort='desc'>Archive Date</th>"
|
||||||
|
+ "<th data-defaultsort='disabled'></th><th class='" + ACTION_MENU_CLASS + "' data-defaultsort='disabled'>Actions</th>"
|
||||||
|
+ "</tr></thead>";
|
||||||
|
|
||||||
html += backups_table_head;
|
html += backups_table_head;
|
||||||
html += "<tbody id='" + AUTOMATIC_ARCHIVES_TBODY_ID + "'></tbody></table>";
|
html += "<tbody id='" + AUTOMATIC_ARCHIVES_TBODY_ID + "'></tbody></table>";
|
||||||
|
@ -95,7 +112,11 @@ $(document).ready(function(){
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
html += "<table class='table sortable' id='" + MANUAL_ARCHIVES_TABLE_ID + "'>";
|
html += "<table class='table sortable' id='" + MANUAL_ARCHIVES_TABLE_ID + "'>";
|
||||||
html += backups_table_head;
|
html += backups_table_head;
|
||||||
html += "<tbody id='" + MANUAL_ARCHIVES_TBODY_ID + "'></tbody></table>";
|
html += "<tbody id='" + MANUAL_ARCHIVES_TBODY_ID + "'></tbody></table></div>";
|
||||||
|
|
||||||
|
html += "<div class='form-group' id='" + CONTENT_ARCHIVES_ERROR_ID + "' style='display:none;'>"
|
||||||
|
+ "<span class='help-block'>There was a problem loading your list of automatic and manual content archives. "
|
||||||
|
+ "Please reload the page to try again.</span></div>";
|
||||||
|
|
||||||
// put the base HTML in the content archives panel
|
// put the base HTML in the content archives panel
|
||||||
$('#' + Settings.CONTENT_ARCHIVES_PANEL_ID + ' .panel-body').html(html);
|
$('#' + Settings.CONTENT_ARCHIVES_PANEL_ID + ' .panel-body').html(html);
|
||||||
|
@ -104,8 +125,9 @@ $(document).ready(function(){
|
||||||
var BACKUP_RESTORE_LINK_CLASS = 'restore-backup';
|
var BACKUP_RESTORE_LINK_CLASS = 'restore-backup';
|
||||||
var BACKUP_DOWNLOAD_LINK_CLASS = 'download-backup';
|
var BACKUP_DOWNLOAD_LINK_CLASS = 'download-backup';
|
||||||
var BACKUP_DELETE_LINK_CLASS = 'delete-backup';
|
var BACKUP_DELETE_LINK_CLASS = 'delete-backup';
|
||||||
|
var ACTIVE_BACKUP_ROW_CLASS = 'active-backup';
|
||||||
|
|
||||||
function reloadLatestBackups() {
|
function reloadBackupInformation() {
|
||||||
// make a GET request to get backup information to populate the table
|
// make a GET request to get backup information to populate the table
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/api/backups',
|
url: '/api/backups',
|
||||||
|
@ -123,41 +145,92 @@ $(document).ready(function(){
|
||||||
// populate the backups tables with the backups
|
// populate the backups tables with the backups
|
||||||
function createBackupTableRow(backup) {
|
function createBackupTableRow(backup) {
|
||||||
return "<tr data-backup-id='" + backup.id + "' data-backup-name='" + backup.name + "'>"
|
return "<tr data-backup-id='" + backup.id + "' data-backup-name='" + backup.name + "'>"
|
||||||
+ "<td data-value='" + backup.name.toLowerCase() + "'>" + backup.name + "</td><td data-dateformat='lll'>"
|
+ "<td data-value='" + backup.name.toLowerCase() + "'>" + backup.name + "</td><td data-value='" + backup.createdAtMillis + "'>"
|
||||||
+ moment(backup.createdAtMillis).format('lll')
|
+ moment(backup.createdAtMillis).format('lll')
|
||||||
+ "</td><td class='text-right'>"
|
+ "</td><td class='backup-status'></td><td class='" + ACTION_MENU_CLASS + "'>"
|
||||||
+ "<div class='dropdown'><div class='dropdown-toggle' data-toggle='dropdown' aria-expanded='false'><span class='glyphicon glyphicon-option-vertical'></span></div>"
|
+ "<div class='dropdown'><div class='dropdown-toggle' data-toggle='dropdown' aria-expanded='false'><span class='glyphicon glyphicon-option-vertical'></span></div>"
|
||||||
+ "<ul class='dropdown-menu dropdown-menu-right'>"
|
+ "<ul class='dropdown-menu dropdown-menu-right'>"
|
||||||
+ "<li><a class='" + BACKUP_RESTORE_LINK_CLASS + "' href='#'>Restore from here</a></li><li class='divider'></li>"
|
+ "<li><a class='" + BACKUP_RESTORE_LINK_CLASS + "' href='#'>Restore from here</a></li><li class='divider'></li>"
|
||||||
+ "<li><a class='" + BACKUP_DOWNLOAD_LINK_CLASS + "' href='#'>Download</a></li><li class='divider'></li>"
|
+ "<li><a class='" + BACKUP_DOWNLOAD_LINK_CLASS + "' href='/api/backups/" + backup.id + "'>Download</a></li><li class='divider'></li>"
|
||||||
+ "<li><a class='" + BACKUP_DELETE_LINK_CLASS + "' href='/api/backups/" + backup.id + "' target='_blank'>Delete</a></li></ul></div></td>";
|
+ "<li><a class='" + BACKUP_DELETE_LINK_CLASS + "' href='#' target='_blank'>Delete</a></li></ul></div></td>";
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateProgressBars($progressBar, value) {
|
||||||
|
$progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%');
|
||||||
|
$progressBar.find('.sr-only').html(data.status.recoveryProgress + "% Complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
// before we add any new rows and update existing ones
|
||||||
|
// remove our flag for active rows
|
||||||
|
$('.' + ACTIVE_BACKUP_ROW_CLASS).removeClass(ACTIVE_BACKUP_ROW_CLASS);
|
||||||
|
|
||||||
|
function updateOrAddTableRow(backup, tableBodyID) {
|
||||||
|
// check for a backup with this ID
|
||||||
|
var $backupRow = $("tr[data-backup-id='" + backup.id + "']");
|
||||||
|
|
||||||
|
if ($backupRow.length == 0) {
|
||||||
|
// create a new row and then add it to the table
|
||||||
|
$backupRow = $(createBackupTableRow(backup));
|
||||||
|
$('#' + tableBodyID).append($backupRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the row status column depending on if it is available or recovering
|
||||||
|
if (!backup.isAvailable) {
|
||||||
|
// add a progress bar to the status row for availability
|
||||||
|
$backupRow.find('td.backup-status').html(progressBarHTML('availability', 'Archiving'));
|
||||||
|
|
||||||
|
// set the value of the progress bar based on availability progress
|
||||||
|
updateProgressBars($backupRow.find('.progress-bar'), backup.availabilityProgress * 100);
|
||||||
|
} else if (backup.id == data.status.recoveringBackupId) {
|
||||||
|
// add a progress bar to the status row for recovery
|
||||||
|
$backupRow.find('td.backup-status').html(progressBarHTML('recovery', 'Restoring'));
|
||||||
|
} else {
|
||||||
|
// no special status for this row, use an empty status column
|
||||||
|
$backupRow.find('td.backup-status').html('');
|
||||||
|
}
|
||||||
|
|
||||||
|
$backupRow.find('td.' + ACTION_MENU_CLASS + ' .dropdown').toggle(backup.isAvailable);
|
||||||
|
|
||||||
|
$backupRow.addClass(ACTIVE_BACKUP_ROW_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
var automaticRows = "";
|
var automaticRows = "";
|
||||||
|
|
||||||
if (automaticBackups.length > 0) {
|
if (automaticBackups.length > 0) {
|
||||||
for (var backupIndex in automaticBackups) {
|
for (var backupIndex in automaticBackups) {
|
||||||
// create a table row for this backup and add it to the rows we'll put in the table body
|
updateOrAddTableRow(automaticBackups[backupIndex], AUTOMATIC_ARCHIVES_TBODY_ID);
|
||||||
automaticRows += createBackupTableRow(automaticBackups[backupIndex]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#' + AUTOMATIC_ARCHIVES_TBODY_ID).html(automaticRows);
|
|
||||||
|
|
||||||
var manualRows = "";
|
|
||||||
|
|
||||||
if (manualBackups.length > 0) {
|
if (manualBackups.length > 0) {
|
||||||
for (var backupIndex in manualBackups) {
|
for (var backupIndex in manualBackups) {
|
||||||
// create a table row for this backup and add it to the rows we'll put in the table body
|
updateOrAddTableRow(manualBackups[backupIndex], MANUAL_ARCHIVES_TBODY_ID);
|
||||||
manualRows += createBackupTableRow(manualBackups[backupIndex]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#' + MANUAL_ARCHIVES_TBODY_ID).html(manualRows);
|
// at this point, any rows that no longer have the ACTIVE_BACKUP_ROW_CLASS
|
||||||
|
// are deleted backups, so we remove them from the table
|
||||||
|
$('#' + CONTENT_ARCHIVES_NORMAL_ID + ' tbody tr:not(.' + ACTIVE_BACKUP_ROW_CLASS + ')').remove();
|
||||||
|
|
||||||
|
// check if the restore action on all rows should be enabled or disabled
|
||||||
|
$('.' + BACKUP_RESTORE_LINK_CLASS).parent().toggleClass('disabled', data.status.isRecovering);
|
||||||
|
|
||||||
|
// hide or show the manual content upload file and button depending on our recovering status
|
||||||
|
$('#' + UPLOAD_CONTENT_ALLOWED_DIV_ID).toggle(!data.status.isRecovering);
|
||||||
|
$('#' + UPLOAD_CONTENT_RECOVERING_DIV_ID).toggle(data.status.isRecovering);
|
||||||
|
|
||||||
|
// update the progress bars for current restore status
|
||||||
|
if (data.status.isRecovering) {
|
||||||
|
updateProgressBars($('.recovery.progress-bar'), data.status.recoveryProgress * 100);
|
||||||
|
}
|
||||||
|
|
||||||
// tell bootstrap sortable to update for the new rows
|
// tell bootstrap sortable to update for the new rows
|
||||||
$.bootstrapSortable({ applyLast: true });
|
$.bootstrapSortable({ applyLast: true });
|
||||||
|
|
||||||
|
$('#' + CONTENT_ARCHIVES_NORMAL_ID).toggle(true);
|
||||||
|
$('#' + CONTENT_ARCHIVES_ERROR_ID).toggle(false);
|
||||||
|
|
||||||
}).fail(function(){
|
}).fail(function(){
|
||||||
// we've hit the very rare case where we couldn't load the list of backups from the domain server
|
// we've hit the very rare case where we couldn't load the list of backups from the domain server
|
||||||
|
|
||||||
|
@ -167,11 +240,8 @@ $(document).ready(function(){
|
||||||
|
|
||||||
// replace the content archives panel with a simple error message
|
// replace the content archives panel with a simple error message
|
||||||
// stating that the user should reload the page
|
// stating that the user should reload the page
|
||||||
$('#' + Settings.CONTENT_ARCHIVES_PANEL_ID + ' .panel-body').html(
|
$('#' + CONTENT_ARCHIVES_NORMAL_ID).toggle(false);
|
||||||
"<div class='form-group'>" +
|
$('#' + CONTENT_ARCHIVES_ERROR_ID).toggle(true);
|
||||||
"<span class='help-block'>There was a problem loading your list of automatic and manual content archives. Please reload the page to try again.</span>" +
|
|
||||||
"</div>"
|
|
||||||
);
|
|
||||||
|
|
||||||
}).always(function(){
|
}).always(function(){
|
||||||
// toggle showing or hiding the tables depending on if they have entries
|
// toggle showing or hiding the tables depending on if they have entries
|
||||||
|
@ -197,12 +267,11 @@ $(document).ready(function(){
|
||||||
"Restore content",
|
"Restore content",
|
||||||
function() {
|
function() {
|
||||||
// show a spinner while we send off our request
|
// show a spinner while we send off our request
|
||||||
showSpinnerAlert("Restoring Content Archive " + backupName);
|
showSpinnerAlert("Starting restore of " + backupName);
|
||||||
|
|
||||||
// setup an AJAX POST to request content restore
|
// setup an AJAX POST to request content restore
|
||||||
$.post('/api/backups/recover/' + backupID).done(function(data, textStatus, jqXHR) {
|
$.post('/api/backups/recover/' + backupID).done(function(data, textStatus, jqXHR) {
|
||||||
swal.close();
|
swal.close();
|
||||||
showRestartModal();
|
|
||||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||||
showErrorMessage(
|
showErrorMessage(
|
||||||
"Error",
|
"Error",
|
||||||
|
@ -247,7 +316,7 @@ $(document).ready(function(){
|
||||||
}).always(function(){
|
}).always(function(){
|
||||||
// reload the list of content archives in case we deleted a backup
|
// reload the list of content archives in case we deleted a backup
|
||||||
// or it's no longer an available backup for some other reason
|
// or it's no longer an available backup for some other reason
|
||||||
reloadLatestBackups();
|
reloadBackupInformation();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -306,7 +375,7 @@ $(document).ready(function(){
|
||||||
}).done(function(data) {
|
}).done(function(data) {
|
||||||
// since we successfully setup a new content archive, reload the table of archives
|
// since we successfully setup a new content archive, reload the table of archives
|
||||||
// which should show that this archive is pending creation
|
// which should show that this archive is pending creation
|
||||||
reloadLatestBackups();
|
reloadBackupInformation();
|
||||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -322,6 +391,9 @@ $(document).ready(function(){
|
||||||
setupContentArchives();
|
setupContentArchives();
|
||||||
|
|
||||||
// load the latest backups immediately
|
// load the latest backups immediately
|
||||||
reloadLatestBackups();
|
reloadBackupInformation();
|
||||||
|
|
||||||
|
// setup a timer to reload them every 5 seconds
|
||||||
|
setInterval(reloadBackupInformation, 5000);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -466,6 +466,11 @@ tr.gray-tr {
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table .action-menu {
|
||||||
|
text-align: right;
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-toggle span.glyphicon-option-vertical {
|
.dropdown-toggle span.glyphicon-option-vertical {
|
||||||
font-size: 110%;
|
font-size: 110%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -263,7 +263,7 @@ $(document).ready(function(){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#' + Settings.FORM_ID).on('change keyup paste', '.' + Settings.TRIGGER_CHANGE_CLASS , function(e){
|
$('#' + Settings.FORM_ID).on('change input propertychange', '.' + Settings.TRIGGER_CHANGE_CLASS , function(e){
|
||||||
// this input was changed, add the changed data attribute to it
|
// this input was changed, add the changed data attribute to it
|
||||||
$(this).attr('data-changed', true);
|
$(this).attr('data-changed', true);
|
||||||
|
|
||||||
|
@ -838,7 +838,7 @@ function addTableRow(row) {
|
||||||
var keyInput = row.children(".key").children("input");
|
var keyInput = row.children(".key").children("input");
|
||||||
|
|
||||||
// whenever the keyInput changes, re-badge for differences
|
// whenever the keyInput changes, re-badge for differences
|
||||||
keyInput.on('change keyup paste', function(e){
|
keyInput.on('change input propertychange', function(e){
|
||||||
// update siblings in the row to have the correct name
|
// update siblings in the row to have the correct name
|
||||||
var currentKey = $(this).val();
|
var currentKey = $(this).val();
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,8 @@ DomainContentBackupManager::DomainContentBackupManager(const QString& backupDire
|
||||||
_persistInterval(persistInterval),
|
_persistInterval(persistInterval),
|
||||||
_lastCheck(usecTimestampNow())
|
_lastCheck(usecTimestampNow())
|
||||||
{
|
{
|
||||||
|
setObjectName("DomainContentBackupManager");
|
||||||
|
|
||||||
// Make sure the backup directory exists.
|
// Make sure the backup directory exists.
|
||||||
QDir(_backupDirectory).mkpath(".");
|
QDir(_backupDirectory).mkpath(".");
|
||||||
|
|
||||||
|
@ -309,6 +311,7 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise,
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainContentBackupManager::recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup) {
|
void DomainContentBackupManager::recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup) {
|
||||||
|
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QMetaObject::invokeMethod(this, "recoverFromUploadedBackup", Q_ARG(MiniPromise::Promise, promise),
|
QMetaObject::invokeMethod(this, "recoverFromUploadedBackup", Q_ARG(MiniPromise::Promise, promise),
|
||||||
Q_ARG(QByteArray, uploadedBackup));
|
Q_ARG(QByteArray, uploadedBackup));
|
||||||
|
|
|
@ -435,10 +435,11 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
||||||
if (!userPerms.can(NodePermissions::Permission::canConnectPastMaxCapacity) && !isWithinMaxCapacity()) {
|
if (!userPerms.can(NodePermissions::Permission::canConnectPastMaxCapacity) && !isWithinMaxCapacity()) {
|
||||||
// we can't allow this user to connect because we are at max capacity
|
// we can't allow this user to connect because we are at max capacity
|
||||||
QString redirectOnMaxCapacity;
|
QString redirectOnMaxCapacity;
|
||||||
const QVariant* redirectOnMaxCapacityVariant =
|
|
||||||
valueForKeyPath(_server->_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY_REDIRECT_LOCATION);
|
QVariant redirectOnMaxCapacityVariant =
|
||||||
if (redirectOnMaxCapacityVariant && redirectOnMaxCapacityVariant->canConvert<QString>()) {
|
_server->_settingsManager.valueForKeyPath(MAXIMUM_USER_CAPACITY_REDIRECT_LOCATION);
|
||||||
redirectOnMaxCapacity = redirectOnMaxCapacityVariant->toString();
|
if (redirectOnMaxCapacityVariant.canConvert<QString>()) {
|
||||||
|
redirectOnMaxCapacity = redirectOnMaxCapacityVariant.toString();
|
||||||
qDebug() << "Redirection domain:" << redirectOnMaxCapacity;
|
qDebug() << "Redirection domain:" << redirectOnMaxCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,9 +611,9 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
||||||
|
|
||||||
bool DomainGatekeeper::isWithinMaxCapacity() {
|
bool DomainGatekeeper::isWithinMaxCapacity() {
|
||||||
// find out what our maximum capacity is
|
// find out what our maximum capacity is
|
||||||
const QVariant* maximumUserCapacityVariant =
|
QVariant maximumUserCapacityVariant =
|
||||||
valueForKeyPath(_server->_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY);
|
_server->_settingsManager.valueForKeyPath(MAXIMUM_USER_CAPACITY);
|
||||||
unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0;
|
unsigned int maximumUserCapacity = !maximumUserCapacityVariant.isValid() ? maximumUserCapacityVariant.toUInt() : 0;
|
||||||
|
|
||||||
if (maximumUserCapacity > 0) {
|
if (maximumUserCapacity > 0) {
|
||||||
unsigned int connectedUsers = _server->countConnectedUsers();
|
unsigned int connectedUsers = _server->countConnectedUsers();
|
||||||
|
|
|
@ -84,21 +84,22 @@ void DomainMetadata::descriptorsChanged() {
|
||||||
// get descriptors
|
// get descriptors
|
||||||
assert(_metadata[DESCRIPTORS].canConvert<QVariantMap>());
|
assert(_metadata[DESCRIPTORS].canConvert<QVariantMap>());
|
||||||
auto& state = *static_cast<QVariantMap*>(_metadata[DESCRIPTORS].data());
|
auto& state = *static_cast<QVariantMap*>(_metadata[DESCRIPTORS].data());
|
||||||
auto& settings = static_cast<DomainServer*>(parent())->_settingsManager.getSettingsMap();
|
|
||||||
auto& descriptors = static_cast<DomainServer*>(parent())->_settingsManager.getDescriptorsMap();
|
static const QString DESCRIPTORS_GROUP_KEYPATH = "descriptors";
|
||||||
|
auto descriptorsMap = static_cast<DomainServer*>(parent())->_settingsManager.valueForKeyPath(DESCRIPTORS).toMap();
|
||||||
|
|
||||||
// copy simple descriptors (description/maturity)
|
// copy simple descriptors (description/maturity)
|
||||||
state[Descriptors::DESCRIPTION] = descriptors[Descriptors::DESCRIPTION];
|
state[Descriptors::DESCRIPTION] = descriptorsMap[Descriptors::DESCRIPTION];
|
||||||
state[Descriptors::MATURITY] = descriptors[Descriptors::MATURITY];
|
state[Descriptors::MATURITY] = descriptorsMap[Descriptors::MATURITY];
|
||||||
|
|
||||||
// copy array descriptors (hosts/tags)
|
// copy array descriptors (hosts/tags)
|
||||||
state[Descriptors::HOSTS] = descriptors[Descriptors::HOSTS].toList();
|
state[Descriptors::HOSTS] = descriptorsMap[Descriptors::HOSTS].toList();
|
||||||
state[Descriptors::TAGS] = descriptors[Descriptors::TAGS].toList();
|
state[Descriptors::TAGS] = descriptorsMap[Descriptors::TAGS].toList();
|
||||||
|
|
||||||
// parse capacity
|
// parse capacity
|
||||||
static const QString CAPACITY = "security.maximum_user_capacity";
|
static const QString CAPACITY = "security.maximum_user_capacity";
|
||||||
const QVariant* capacityVariant = valueForKeyPath(settings, CAPACITY);
|
QVariant capacityVariant = static_cast<DomainServer*>(parent())->_settingsManager.valueForKeyPath(CAPACITY);
|
||||||
unsigned int capacity = capacityVariant ? capacityVariant->toUInt() : 0;
|
unsigned int capacity = capacityVariant.isValid() ? capacityVariant.toUInt() : 0;
|
||||||
state[Descriptors::CAPACITY] = capacity;
|
state[Descriptors::CAPACITY] = capacity;
|
||||||
|
|
||||||
#if DEV_BUILD || PR_BUILD
|
#if DEV_BUILD || PR_BUILD
|
||||||
|
|
|
@ -75,8 +75,8 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
||||||
std::initializer_list<QString> optionalData,
|
std::initializer_list<QString> optionalData,
|
||||||
bool requireAccessToken) {
|
bool requireAccessToken) {
|
||||||
|
|
||||||
auto accessTokenVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ACCESS_TOKEN_KEY_PATH);
|
auto accessTokenVariant = _settingsManager.valueForKeyPath(ACCESS_TOKEN_KEY_PATH);
|
||||||
if (accessTokenVariant == nullptr && requireAccessToken) {
|
if (!accessTokenVariant.isValid() && requireAccessToken) {
|
||||||
connection->respond(HTTPConnection::StatusCode400, "User access token has not been set");
|
connection->respond(HTTPConnection::StatusCode400, "User access token has not been set");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -112,8 +112,8 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
||||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
if (accessTokenVariant != nullptr) {
|
if (accessTokenVariant.isValid()) {
|
||||||
auto accessTokenHeader = QString("Bearer ") + accessTokenVariant->toString();
|
auto accessTokenHeader = QString("Bearer ") + accessTokenVariant.toString();
|
||||||
req.setRawHeader("Authorization", accessTokenHeader.toLatin1());
|
req.setRawHeader("Authorization", accessTokenHeader.toLatin1());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,6 +380,11 @@ void DomainServer::parseCommandLine() {
|
||||||
DomainServer::~DomainServer() {
|
DomainServer::~DomainServer() {
|
||||||
qInfo() << "Domain Server is shutting down.";
|
qInfo() << "Domain Server is shutting down.";
|
||||||
|
|
||||||
|
if (_contentManager) {
|
||||||
|
_contentManager->aboutToFinish();
|
||||||
|
_contentManager->terminate();
|
||||||
|
}
|
||||||
|
|
||||||
// cleanup the AssetClient thread
|
// cleanup the AssetClient thread
|
||||||
DependencyManager::destroy<AssetClient>();
|
DependencyManager::destroy<AssetClient>();
|
||||||
_assetClientThread.quit();
|
_assetClientThread.quit();
|
||||||
|
@ -387,11 +392,6 @@ DomainServer::~DomainServer() {
|
||||||
|
|
||||||
// destroy the LimitedNodeList before the DomainServer QCoreApplication is down
|
// destroy the LimitedNodeList before the DomainServer QCoreApplication is down
|
||||||
DependencyManager::destroy<LimitedNodeList>();
|
DependencyManager::destroy<LimitedNodeList>();
|
||||||
|
|
||||||
if (_contentManager) {
|
|
||||||
_contentManager->aboutToFinish();
|
|
||||||
_contentManager->terminate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::queuedQuit(QString quitMessage, int exitCode) {
|
void DomainServer::queuedQuit(QString quitMessage, int exitCode) {
|
||||||
|
@ -417,8 +417,8 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() {
|
||||||
const QString X509_PRIVATE_KEY_OPTION = "key";
|
const QString X509_PRIVATE_KEY_OPTION = "key";
|
||||||
const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE";
|
const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE";
|
||||||
|
|
||||||
QString certPath = _settingsManager.getSettingsMap().value(X509_CERTIFICATE_OPTION).toString();
|
QString certPath = _settingsManager.valueForKeyPath(X509_CERTIFICATE_OPTION).toString();
|
||||||
QString keyPath = _settingsManager.getSettingsMap().value(X509_PRIVATE_KEY_OPTION).toString();
|
QString keyPath = _settingsManager.valueForKeyPath(X509_PRIVATE_KEY_OPTION).toString();
|
||||||
|
|
||||||
if (!certPath.isEmpty() && !keyPath.isEmpty()) {
|
if (!certPath.isEmpty() && !keyPath.isEmpty()) {
|
||||||
// the user wants to use the following cert and key for HTTPS
|
// the user wants to use the following cert and key for HTTPS
|
||||||
|
@ -461,8 +461,7 @@ bool DomainServer::optionallySetupOAuth() {
|
||||||
const QString OAUTH_CLIENT_SECRET_ENV = "DOMAIN_SERVER_CLIENT_SECRET";
|
const QString OAUTH_CLIENT_SECRET_ENV = "DOMAIN_SERVER_CLIENT_SECRET";
|
||||||
const QString REDIRECT_HOSTNAME_OPTION = "hostname";
|
const QString REDIRECT_HOSTNAME_OPTION = "hostname";
|
||||||
|
|
||||||
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
_oauthProviderURL = QUrl(_settingsManager.valueForKeyPath(OAUTH_PROVIDER_URL_OPTION).toString());
|
||||||
_oauthProviderURL = QUrl(settingsMap.value(OAUTH_PROVIDER_URL_OPTION).toString());
|
|
||||||
|
|
||||||
// if we don't have an oauth provider URL then we default to the default node auth url
|
// if we don't have an oauth provider URL then we default to the default node auth url
|
||||||
if (_oauthProviderURL.isEmpty()) {
|
if (_oauthProviderURL.isEmpty()) {
|
||||||
|
@ -472,9 +471,9 @@ bool DomainServer::optionallySetupOAuth() {
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
accountManager->setAuthURL(_oauthProviderURL);
|
accountManager->setAuthURL(_oauthProviderURL);
|
||||||
|
|
||||||
_oauthClientID = settingsMap.value(OAUTH_CLIENT_ID_OPTION).toString();
|
_oauthClientID = _settingsManager.valueForKeyPath(OAUTH_CLIENT_ID_OPTION).toString();
|
||||||
_oauthClientSecret = QProcessEnvironment::systemEnvironment().value(OAUTH_CLIENT_SECRET_ENV);
|
_oauthClientSecret = QProcessEnvironment::systemEnvironment().value(OAUTH_CLIENT_SECRET_ENV);
|
||||||
_hostname = settingsMap.value(REDIRECT_HOSTNAME_OPTION).toString();
|
_hostname = _settingsManager.valueForKeyPath(REDIRECT_HOSTNAME_OPTION).toString();
|
||||||
|
|
||||||
if (!_oauthClientID.isEmpty()) {
|
if (!_oauthClientID.isEmpty()) {
|
||||||
if (_oauthProviderURL.isEmpty()
|
if (_oauthProviderURL.isEmpty()
|
||||||
|
@ -499,11 +498,11 @@ static const QString METAVERSE_DOMAIN_ID_KEY_PATH = "metaverse.id";
|
||||||
|
|
||||||
void DomainServer::getTemporaryName(bool force) {
|
void DomainServer::getTemporaryName(bool force) {
|
||||||
// check if we already have a domain ID
|
// check if we already have a domain ID
|
||||||
const QVariant* idValueVariant = valueForKeyPath(_settingsManager.getSettingsMap(), METAVERSE_DOMAIN_ID_KEY_PATH);
|
QVariant idValueVariant = _settingsManager.valueForKeyPath(METAVERSE_DOMAIN_ID_KEY_PATH);
|
||||||
|
|
||||||
qInfo() << "Requesting temporary domain name";
|
qInfo() << "Requesting temporary domain name";
|
||||||
if (idValueVariant) {
|
if (idValueVariant.isValid()) {
|
||||||
qDebug() << "A domain ID is already present in domain-server settings:" << idValueVariant->toString();
|
qDebug() << "A domain ID is already present in domain-server settings:" << idValueVariant.toString();
|
||||||
if (force) {
|
if (force) {
|
||||||
qDebug() << "Requesting temporary domain name to replace current ID:" << getID();
|
qDebug() << "Requesting temporary domain name to replace current ID:" << getID();
|
||||||
} else {
|
} else {
|
||||||
|
@ -543,9 +542,6 @@ void DomainServer::handleTempDomainSuccess(QNetworkReply& requestReply) {
|
||||||
auto settingsDocument = QJsonDocument::fromJson(newSettingsJSON.toUtf8());
|
auto settingsDocument = QJsonDocument::fromJson(newSettingsJSON.toUtf8());
|
||||||
_settingsManager.recurseJSONObjectAndOverwriteSettings(settingsDocument.object(), DomainSettings);
|
_settingsManager.recurseJSONObjectAndOverwriteSettings(settingsDocument.object(), DomainSettings);
|
||||||
|
|
||||||
// store the new ID and auto networking setting on disk
|
|
||||||
_settingsManager.persistToFile();
|
|
||||||
|
|
||||||
// store the new token to the account info
|
// store the new token to the account info
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
accountManager->setTemporaryDomain(id, key);
|
accountManager->setTemporaryDomain(id, key);
|
||||||
|
@ -647,8 +643,6 @@ void DomainServer::setupNodeListAndAssignments() {
|
||||||
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
|
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
|
||||||
int domainServerPort = localPortValue.toInt();
|
int domainServerPort = localPortValue.toInt();
|
||||||
|
|
||||||
QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
|
||||||
|
|
||||||
int domainServerDTLSPort = INVALID_PORT;
|
int domainServerDTLSPort = INVALID_PORT;
|
||||||
|
|
||||||
if (_isUsingDTLS) {
|
if (_isUsingDTLS) {
|
||||||
|
@ -656,8 +650,9 @@ void DomainServer::setupNodeListAndAssignments() {
|
||||||
|
|
||||||
const QString CUSTOM_DTLS_PORT_OPTION = "dtls-port";
|
const QString CUSTOM_DTLS_PORT_OPTION = "dtls-port";
|
||||||
|
|
||||||
if (settingsMap.contains(CUSTOM_DTLS_PORT_OPTION)) {
|
auto dtlsPortVariant = _settingsManager.valueForKeyPath(CUSTOM_DTLS_PORT_OPTION);
|
||||||
domainServerDTLSPort = (unsigned short) settingsMap.value(CUSTOM_DTLS_PORT_OPTION).toUInt();
|
if (dtlsPortVariant.isValid()) {
|
||||||
|
domainServerDTLSPort = (unsigned short) dtlsPortVariant.toUInt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,9 +682,9 @@ void DomainServer::setupNodeListAndAssignments() {
|
||||||
nodeList->setSessionUUID(_overridingDomainID);
|
nodeList->setSessionUUID(_overridingDomainID);
|
||||||
isMetaverseDomain = true; // assume metaverse domain
|
isMetaverseDomain = true; // assume metaverse domain
|
||||||
} else {
|
} else {
|
||||||
const QVariant* idValueVariant = valueForKeyPath(settingsMap, METAVERSE_DOMAIN_ID_KEY_PATH);
|
QVariant idValueVariant = _settingsManager.valueForKeyPath(METAVERSE_DOMAIN_ID_KEY_PATH);
|
||||||
if (idValueVariant) {
|
if (idValueVariant.isValid()) {
|
||||||
nodeList->setSessionUUID(idValueVariant->toString());
|
nodeList->setSessionUUID(idValueVariant.toString());
|
||||||
isMetaverseDomain = true; // if we have an ID, we'll assume we're a metaverse domain
|
isMetaverseDomain = true; // if we have an ID, we'll assume we're a metaverse domain
|
||||||
} else {
|
} else {
|
||||||
nodeList->setSessionUUID(QUuid::createUuid()); // Use random UUID
|
nodeList->setSessionUUID(QUuid::createUuid()); // Use random UUID
|
||||||
|
@ -758,10 +753,10 @@ bool DomainServer::resetAccountManagerAccessToken() {
|
||||||
QString accessToken = QProcessEnvironment::systemEnvironment().value(ENV_ACCESS_TOKEN_KEY);
|
QString accessToken = QProcessEnvironment::systemEnvironment().value(ENV_ACCESS_TOKEN_KEY);
|
||||||
|
|
||||||
if (accessToken.isEmpty()) {
|
if (accessToken.isEmpty()) {
|
||||||
const QVariant* accessTokenVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ACCESS_TOKEN_KEY_PATH);
|
QVariant accessTokenVariant = _settingsManager.valueForKeyPath(ACCESS_TOKEN_KEY_PATH);
|
||||||
|
|
||||||
if (accessTokenVariant && accessTokenVariant->canConvert(QMetaType::QString)) {
|
if (accessTokenVariant.isValid() && accessTokenVariant.canConvert(QMetaType::QString)) {
|
||||||
accessToken = accessTokenVariant->toString();
|
accessToken = accessTokenVariant.toString();
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "No access token is present. Some operations that use the metaverse API will fail.";
|
qWarning() << "No access token is present. Some operations that use the metaverse API will fail.";
|
||||||
qDebug() << "Set an access token via the web interface, in your user config"
|
qDebug() << "Set an access token via the web interface, in your user config"
|
||||||
|
@ -892,31 +887,26 @@ void DomainServer::updateICEServerAddresses() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes) {
|
void DomainServer::parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes) {
|
||||||
const QString ASSIGNMENT_CONFIG_REGEX_STRING = "config-([\\d]+)";
|
const QString ASSIGNMENT_CONFIG_PREFIX = "config-";
|
||||||
QRegExp assignmentConfigRegex(ASSIGNMENT_CONFIG_REGEX_STRING);
|
|
||||||
|
|
||||||
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
|
||||||
|
|
||||||
// scan for assignment config keys
|
// scan for assignment config keys
|
||||||
QStringList variantMapKeys = settingsMap.keys();
|
for (int i = 0; i < Assignment::AllTypes; ++i) {
|
||||||
int configIndex = variantMapKeys.indexOf(assignmentConfigRegex);
|
QVariant assignmentConfigVariant = _settingsManager.valueOrDefaultValueForKeyPath(ASSIGNMENT_CONFIG_PREFIX + QString::number(i));
|
||||||
|
|
||||||
while (configIndex != -1) {
|
if (assignmentConfigVariant.isValid()) {
|
||||||
// figure out which assignment type this matches
|
// figure out which assignment type this matches
|
||||||
Assignment::Type assignmentType = (Assignment::Type) assignmentConfigRegex.cap(1).toInt();
|
Assignment::Type assignmentType = static_cast<Assignment::Type>(i);
|
||||||
|
|
||||||
if (assignmentType < Assignment::AllTypes && !excludedTypes.contains(assignmentType)) {
|
if (!excludedTypes.contains(assignmentType)) {
|
||||||
QVariant mapValue = settingsMap[variantMapKeys[configIndex]];
|
QVariantList assignmentList = assignmentConfigVariant.toList();
|
||||||
QVariantList assignmentList = mapValue.toList();
|
|
||||||
|
|
||||||
if (assignmentType != Assignment::AgentType) {
|
if (assignmentType != Assignment::AgentType) {
|
||||||
createStaticAssignmentsForType(assignmentType, assignmentList);
|
createStaticAssignmentsForType(assignmentType, assignmentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
excludedTypes.insert(assignmentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
excludedTypes.insert(assignmentType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configIndex = variantMapKeys.indexOf(assignmentConfigRegex, configIndex + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -928,10 +918,10 @@ void DomainServer::addStaticAssignmentToAssignmentHash(Assignment* newAssignment
|
||||||
|
|
||||||
void DomainServer::populateStaticScriptedAssignmentsFromSettings() {
|
void DomainServer::populateStaticScriptedAssignmentsFromSettings() {
|
||||||
const QString PERSISTENT_SCRIPTS_KEY_PATH = "scripts.persistent_scripts";
|
const QString PERSISTENT_SCRIPTS_KEY_PATH = "scripts.persistent_scripts";
|
||||||
const QVariant* persistentScriptsVariant = valueForKeyPath(_settingsManager.getSettingsMap(), PERSISTENT_SCRIPTS_KEY_PATH);
|
QVariant persistentScriptsVariant = _settingsManager.valueOrDefaultValueForKeyPath(PERSISTENT_SCRIPTS_KEY_PATH);
|
||||||
|
|
||||||
if (persistentScriptsVariant) {
|
if (persistentScriptsVariant.isValid()) {
|
||||||
QVariantList persistentScriptsList = persistentScriptsVariant->toList();
|
QVariantList persistentScriptsList = persistentScriptsVariant.toList();
|
||||||
foreach(const QVariant& persistentScriptVariant, persistentScriptsList) {
|
foreach(const QVariant& persistentScriptVariant, persistentScriptsList) {
|
||||||
QVariantMap persistentScript = persistentScriptVariant.toMap();
|
QVariantMap persistentScript = persistentScriptVariant.toMap();
|
||||||
|
|
||||||
|
@ -1761,7 +1751,7 @@ void DomainServer::processOctreeDataPersistMessage(QSharedPointer<ReceivedMessag
|
||||||
f.write(data);
|
f.write(data);
|
||||||
OctreeUtils::RawOctreeData octreeData;
|
OctreeUtils::RawOctreeData octreeData;
|
||||||
if (OctreeUtils::readOctreeDataInfoFromData(data, &octreeData)) {
|
if (OctreeUtils::readOctreeDataInfoFromData(data, &octreeData)) {
|
||||||
qCDebug(domain_server) << "Wrote new entiteis file" << octreeData.id << octreeData.version;
|
qCDebug(domain_server) << "Wrote new entities file" << octreeData.id << octreeData.version;
|
||||||
} else {
|
} else {
|
||||||
qCDebug(domain_server) << "Failed to read new octree data info";
|
qCDebug(domain_server) << "Failed to read new octree data info";
|
||||||
}
|
}
|
||||||
|
@ -1954,13 +1944,12 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
|
||||||
auto getSetting = [this](QString keyPath, QVariant& value) -> bool {
|
auto getSetting = [this](QString keyPath, QVariant value) -> bool {
|
||||||
QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
|
||||||
QVariant* var = valueForKeyPath(settingsMap, keyPath);
|
value = _settingsManager.valueForKeyPath(keyPath);
|
||||||
if (var == nullptr) {
|
if (!value.isValid()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
value = *var;
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2028,8 +2017,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
|
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
|
||||||
const QString URI_WIZARD = "/wizard/";
|
const QString URI_WIZARD = "/wizard/";
|
||||||
const QString WIZARD_COMPLETED_ONCE_KEY_PATH = "wizard.completed_once";
|
const QString WIZARD_COMPLETED_ONCE_KEY_PATH = "wizard.completed_once";
|
||||||
const QVariant* wizardCompletedOnce = valueForKeyPath(_settingsManager.getSettingsMap(), WIZARD_COMPLETED_ONCE_KEY_PATH);
|
QVariant wizardCompletedOnce = _settingsManager.valueForKeyPath(WIZARD_COMPLETED_ONCE_KEY_PATH);
|
||||||
const bool completedOnce = wizardCompletedOnce && wizardCompletedOnce->toBool();
|
const bool completedOnce = wizardCompletedOnce.isValid() && wizardCompletedOnce.toBool();
|
||||||
|
|
||||||
if (url.path() != URI_WIZARD && url.path().endsWith('/') && !completedOnce) {
|
if (url.path() != URI_WIZARD && url.path().endsWith('/') && !completedOnce) {
|
||||||
// First visit, redirect to the wizard
|
// First visit, redirect to the wizard
|
||||||
|
@ -2326,8 +2315,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else if (url.path() == "/domain_settings") {
|
} else if (url.path() == "/domain_settings") {
|
||||||
auto accessTokenVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ACCESS_TOKEN_KEY_PATH);
|
auto accessTokenVariant = _settingsManager.valueForKeyPath(ACCESS_TOKEN_KEY_PATH);
|
||||||
if (!accessTokenVariant) {
|
if (!accessTokenVariant.isValid()) {
|
||||||
connection->respond(HTTPConnection::StatusCode400);
|
connection->respond(HTTPConnection::StatusCode400);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2360,8 +2349,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
return forwardMetaverseAPIRequest(connection, "/api/v1/domains/" + domainID, "domain",
|
return forwardMetaverseAPIRequest(connection, "/api/v1/domains/" + domainID, "domain",
|
||||||
{ }, { "network_address", "network_port", "label" });
|
{ }, { "network_address", "network_port", "label" });
|
||||||
} else if (url.path() == URI_API_PLACES) {
|
} else if (url.path() == URI_API_PLACES) {
|
||||||
auto accessTokenVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ACCESS_TOKEN_KEY_PATH);
|
auto accessTokenVariant = _settingsManager.valueForKeyPath(ACCESS_TOKEN_KEY_PATH);
|
||||||
if (!accessTokenVariant->isValid()) {
|
if (!accessTokenVariant.isValid()) {
|
||||||
connection->respond(HTTPConnection::StatusCode400, "User access token has not been set");
|
connection->respond(HTTPConnection::StatusCode400, "User access token has not been set");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2409,7 +2398,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/v1/places/" + place_id };
|
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/v1/places/" + place_id };
|
||||||
|
|
||||||
url.setQuery("access_token=" + accessTokenVariant->toString());
|
url.setQuery("access_token=" + accessTokenVariant.toString());
|
||||||
|
|
||||||
QNetworkRequest req(url);
|
QNetworkRequest req(url);
|
||||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||||
|
@ -2604,10 +2593,11 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
||||||
|
|
||||||
const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server.";
|
const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server.";
|
||||||
|
|
||||||
QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
QVariant adminUsersVariant = _settingsManager.valueForKeyPath(ADMIN_USERS_CONFIG_KEY);
|
||||||
|
QVariant adminRolesVariant = _settingsManager.valueForKeyPath(ADMIN_ROLES_CONFIG_KEY);
|
||||||
|
|
||||||
if (!_oauthProviderURL.isEmpty()
|
if (!_oauthProviderURL.isEmpty()
|
||||||
&& (settingsMap.contains(ADMIN_USERS_CONFIG_KEY) || settingsMap.contains(ADMIN_ROLES_CONFIG_KEY))) {
|
&& (adminUsersVariant.isValid() || adminRolesVariant.isValid())) {
|
||||||
QString cookieString = connection->requestHeaders().value(HTTP_COOKIE_HEADER_KEY);
|
QString cookieString = connection->requestHeaders().value(HTTP_COOKIE_HEADER_KEY);
|
||||||
|
|
||||||
const QString COOKIE_UUID_REGEX_STRING = HIFI_SESSION_COOKIE_KEY + "=([\\d\\w-]+)($|;)";
|
const QString COOKIE_UUID_REGEX_STRING = HIFI_SESSION_COOKIE_KEY + "=([\\d\\w-]+)($|;)";
|
||||||
|
@ -2618,7 +2608,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
||||||
cookieUUID = cookieUUIDRegex.cap(1);
|
cookieUUID = cookieUUIDRegex.cap(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueForKeyPath(settingsMap, BASIC_AUTH_USERNAME_KEY_PATH)) {
|
if (_settingsManager.valueForKeyPath(BASIC_AUTH_USERNAME_KEY_PATH).isValid()) {
|
||||||
qDebug() << "Config file contains web admin settings for OAuth and basic HTTP authentication."
|
qDebug() << "Config file contains web admin settings for OAuth and basic HTTP authentication."
|
||||||
<< "These cannot be combined - using OAuth for authentication.";
|
<< "These cannot be combined - using OAuth for authentication.";
|
||||||
}
|
}
|
||||||
|
@ -2628,13 +2618,13 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
||||||
DomainServerWebSessionData sessionData = _cookieSessionHash.value(cookieUUID);
|
DomainServerWebSessionData sessionData = _cookieSessionHash.value(cookieUUID);
|
||||||
QString profileUsername = sessionData.getUsername();
|
QString profileUsername = sessionData.getUsername();
|
||||||
|
|
||||||
if (settingsMap.value(ADMIN_USERS_CONFIG_KEY).toStringList().contains(profileUsername)) {
|
if (_settingsManager.valueForKeyPath(ADMIN_USERS_CONFIG_KEY).toStringList().contains(profileUsername)) {
|
||||||
// this is an authenticated user
|
// this is an authenticated user
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// loop the roles of this user and see if they are in the admin-roles array
|
// loop the roles of this user and see if they are in the admin-roles array
|
||||||
QStringList adminRolesArray = settingsMap.value(ADMIN_ROLES_CONFIG_KEY).toStringList();
|
QStringList adminRolesArray = _settingsManager.valueForKeyPath(ADMIN_ROLES_CONFIG_KEY).toStringList();
|
||||||
|
|
||||||
if (!adminRolesArray.isEmpty()) {
|
if (!adminRolesArray.isEmpty()) {
|
||||||
foreach(const QString& userRole, sessionData.getRoles()) {
|
foreach(const QString& userRole, sessionData.getRoles()) {
|
||||||
|
@ -2679,7 +2669,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
||||||
// we don't know about this user yet, so they are not yet authenticated
|
// we don't know about this user yet, so they are not yet authenticated
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (valueForKeyPath(settingsMap, BASIC_AUTH_USERNAME_KEY_PATH)) {
|
} else if (_settingsManager.valueForKeyPath(BASIC_AUTH_USERNAME_KEY_PATH).isValid()) {
|
||||||
// config file contains username and password combinations for basic auth
|
// config file contains username and password combinations for basic auth
|
||||||
const QByteArray BASIC_AUTH_HEADER_KEY = "Authorization";
|
const QByteArray BASIC_AUTH_HEADER_KEY = "Authorization";
|
||||||
|
|
||||||
|
@ -2698,10 +2688,10 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
||||||
QString headerPassword = credentialList[1];
|
QString headerPassword = credentialList[1];
|
||||||
|
|
||||||
// we've pulled a username and password - now check if there is a match in our basic auth hash
|
// we've pulled a username and password - now check if there is a match in our basic auth hash
|
||||||
QString settingsUsername = valueForKeyPath(settingsMap, BASIC_AUTH_USERNAME_KEY_PATH)->toString();
|
QString settingsUsername = _settingsManager.valueForKeyPath(BASIC_AUTH_USERNAME_KEY_PATH).toString();
|
||||||
const QVariant* settingsPasswordVariant = valueForKeyPath(settingsMap, BASIC_AUTH_PASSWORD_KEY_PATH);
|
QVariant settingsPasswordVariant = _settingsManager.valueForKeyPath(BASIC_AUTH_PASSWORD_KEY_PATH);
|
||||||
|
|
||||||
QString settingsPassword = settingsPasswordVariant ? settingsPasswordVariant->toString() : "";
|
QString settingsPassword = settingsPasswordVariant.isValid() ? settingsPasswordVariant.toString() : "";
|
||||||
QString hexHeaderPassword = headerPassword.isEmpty() ?
|
QString hexHeaderPassword = headerPassword.isEmpty() ?
|
||||||
"" : QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex();
|
"" : QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex();
|
||||||
|
|
||||||
|
@ -2838,13 +2828,14 @@ ReplicationServerInfo serverInformationFromSettings(QVariantMap serverMap, Repli
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::updateReplicationNodes(ReplicationServerDirection direction) {
|
void DomainServer::updateReplicationNodes(ReplicationServerDirection direction) {
|
||||||
auto settings = _settingsManager.getSettingsMap();
|
|
||||||
|
|
||||||
if (settings.contains(BROADCASTING_SETTINGS_KEY)) {
|
auto broadcastSettingsVariant = _settingsManager.valueForKeyPath(BROADCASTING_SETTINGS_KEY);
|
||||||
|
|
||||||
|
if (broadcastSettingsVariant.isValid()) {
|
||||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
std::vector<HifiSockAddr> replicationNodesInSettings;
|
std::vector<HifiSockAddr> replicationNodesInSettings;
|
||||||
|
|
||||||
auto replicationSettings = settings.value(BROADCASTING_SETTINGS_KEY).toMap();
|
auto replicationSettings = broadcastSettingsVariant.toMap();
|
||||||
|
|
||||||
QString serversKey = direction == Upstream ? "upstream_servers" : "downstream_servers";
|
QString serversKey = direction == Upstream ? "upstream_servers" : "downstream_servers";
|
||||||
QString replicationDirection = direction == Upstream ? "upstream" : "downstream";
|
QString replicationDirection = direction == Upstream ? "upstream" : "downstream";
|
||||||
|
@ -2920,13 +2911,12 @@ void DomainServer::updateUpstreamNodes() {
|
||||||
|
|
||||||
void DomainServer::updateReplicatedNodes() {
|
void DomainServer::updateReplicatedNodes() {
|
||||||
// Make sure we have downstream nodes in our list
|
// Make sure we have downstream nodes in our list
|
||||||
auto settings = _settingsManager.getSettingsMap();
|
|
||||||
|
|
||||||
static const QString REPLICATED_USERS_KEY = "users";
|
static const QString REPLICATED_USERS_KEY = "users";
|
||||||
_replicatedUsernames.clear();
|
_replicatedUsernames.clear();
|
||||||
|
|
||||||
if (settings.contains(BROADCASTING_SETTINGS_KEY)) {
|
auto replicationVariant = _settingsManager.valueForKeyPath(BROADCASTING_SETTINGS_KEY);
|
||||||
auto replicationSettings = settings.value(BROADCASTING_SETTINGS_KEY).toMap();
|
if (replicationVariant.isValid()) {
|
||||||
|
auto replicationSettings = replicationVariant.toMap();
|
||||||
if (replicationSettings.contains(REPLICATED_USERS_KEY)) {
|
if (replicationSettings.contains(REPLICATED_USERS_KEY)) {
|
||||||
auto usersSettings = replicationSettings.value(REPLICATED_USERS_KEY).toList();
|
auto usersSettings = replicationSettings.value(REPLICATED_USERS_KEY).toList();
|
||||||
for (auto& username : usersSettings) {
|
for (auto& username : usersSettings) {
|
||||||
|
@ -3114,17 +3104,17 @@ void DomainServer::processPathQueryPacket(QSharedPointer<ReceivedMessage> messag
|
||||||
|
|
||||||
// check out paths in the _configMap to see if we have a match
|
// check out paths in the _configMap to see if we have a match
|
||||||
auto keypath = QString(PATHS_SETTINGS_KEYPATH_FORMAT).arg(SETTINGS_PATHS_KEY).arg(pathQuery);
|
auto keypath = QString(PATHS_SETTINGS_KEYPATH_FORMAT).arg(SETTINGS_PATHS_KEY).arg(pathQuery);
|
||||||
const QVariant* pathMatch = valueForKeyPath(_settingsManager.getSettingsMap(), keypath);
|
QVariant pathMatch = _settingsManager.valueForKeyPath(keypath);
|
||||||
|
|
||||||
if (pathMatch || pathQuery == INDEX_PATH) {
|
if (pathMatch.isValid() || pathQuery == INDEX_PATH) {
|
||||||
// we got a match, respond with the resulting viewpoint
|
// we got a match, respond with the resulting viewpoint
|
||||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
|
||||||
QString responseViewpoint;
|
QString responseViewpoint;
|
||||||
|
|
||||||
// if we didn't match the path BUT this is for the index path then send back our default
|
// if we didn't match the path BUT this is for the index path then send back our default
|
||||||
if (pathMatch) {
|
if (pathMatch.isValid()) {
|
||||||
responseViewpoint = pathMatch->toMap()[PATH_VIEWPOINT_KEY].toString();
|
responseViewpoint = pathMatch.toMap()[PATH_VIEWPOINT_KEY].toString();
|
||||||
} else {
|
} else {
|
||||||
const QString DEFAULT_INDEX_PATH = "/0,0,0/0,0,0,1";
|
const QString DEFAULT_INDEX_PATH = "/0,0,0/0,0,0,1";
|
||||||
responseViewpoint = DEFAULT_INDEX_PATH;
|
responseViewpoint = DEFAULT_INDEX_PATH;
|
||||||
|
|
|
@ -38,6 +38,9 @@
|
||||||
#include "DomainServerNodeData.h"
|
#include "DomainServerNodeData.h"
|
||||||
|
|
||||||
const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json";
|
const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json";
|
||||||
|
const QString SETTINGS_PATH = "/settings";
|
||||||
|
const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json";
|
||||||
|
const QString CONTENT_SETTINGS_PATH_JSON = "/content-settings.json";
|
||||||
|
|
||||||
const QString DESCRIPTION_SETTINGS_KEY = "settings";
|
const QString DESCRIPTION_SETTINGS_KEY = "settings";
|
||||||
const QString SETTING_DEFAULT_KEY = "default";
|
const QString SETTING_DEFAULT_KEY = "default";
|
||||||
|
@ -190,6 +193,9 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<Re
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||||
|
// since we're called from the DomainServerSettingsManager constructor, we don't take a write lock here
|
||||||
|
// even though we change the underlying config map
|
||||||
|
|
||||||
_argumentList = argumentList;
|
_argumentList = argumentList;
|
||||||
|
|
||||||
_configMap.loadConfig(_argumentList);
|
_configMap.loadConfig(_argumentList);
|
||||||
|
@ -448,17 +454,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
||||||
unpackPermissions();
|
unpackPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap& DomainServerSettingsManager::getDescriptorsMap() {
|
|
||||||
static const QString DESCRIPTORS{ "descriptors" };
|
|
||||||
|
|
||||||
auto& settingsMap = getSettingsMap();
|
|
||||||
if (!getSettingsMap().contains(DESCRIPTORS)) {
|
|
||||||
settingsMap.insert(DESCRIPTORS, QVariantMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
return *static_cast<QVariantMap*>(getSettingsMap()[DESCRIPTORS].data());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServerSettingsManager::initializeGroupPermissions(NodePermissionsMap& permissionsRows,
|
void DomainServerSettingsManager::initializeGroupPermissions(NodePermissionsMap& permissionsRows,
|
||||||
QString groupName, NodePermissionsPointer perms) {
|
QString groupName, NodePermissionsPointer perms) {
|
||||||
// this is called when someone has used the domain-settings webpage to add a group. They type the group's name
|
// this is called when someone has used the domain-settings webpage to add a group. They type the group's name
|
||||||
|
@ -487,6 +482,9 @@ void DomainServerSettingsManager::initializeGroupPermissions(NodePermissionsMap&
|
||||||
void DomainServerSettingsManager::packPermissionsForMap(QString mapName,
|
void DomainServerSettingsManager::packPermissionsForMap(QString mapName,
|
||||||
NodePermissionsMap& permissionsRows,
|
NodePermissionsMap& permissionsRows,
|
||||||
QString keyPath) {
|
QString keyPath) {
|
||||||
|
// grab a write lock on the settings mutex since we're about to change the config map
|
||||||
|
QWriteLocker locker(&_settingsLock);
|
||||||
|
|
||||||
// find (or create) the "security" section of the settings map
|
// find (or create) the "security" section of the settings map
|
||||||
QVariant* security = _configMap.valueForKeyPath("security", true);
|
QVariant* security = _configMap.valueForKeyPath("security", true);
|
||||||
if (!security->canConvert(QMetaType::QVariantMap)) {
|
if (!security->canConvert(QMetaType::QVariantMap)) {
|
||||||
|
@ -576,15 +574,20 @@ bool DomainServerSettingsManager::unpackPermissionsForKeypath(const QString& key
|
||||||
|
|
||||||
mapPointer->clear();
|
mapPointer->clear();
|
||||||
|
|
||||||
QVariant* permissions = _configMap.valueForKeyPath(keyPath, true);
|
QVariant permissions = valueOrDefaultValueForKeyPath(keyPath);
|
||||||
if (!permissions->canConvert(QMetaType::QVariantList)) {
|
|
||||||
|
if (!permissions.isValid()) {
|
||||||
|
// we don't have a permissions object to unpack for this keypath, bail
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!permissions.canConvert(QMetaType::QVariantList)) {
|
||||||
qDebug() << "Failed to extract permissions for key path" << keyPath << "from settings.";
|
qDebug() << "Failed to extract permissions for key path" << keyPath << "from settings.";
|
||||||
(*permissions) = QVariantList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needPack = false;
|
bool needPack = false;
|
||||||
|
|
||||||
QList<QVariant> permissionsList = permissions->toList();
|
QList<QVariant> permissionsList = permissions.toList();
|
||||||
foreach (QVariant permsHash, permissionsList) {
|
foreach (QVariant permsHash, permissionsList) {
|
||||||
NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) };
|
NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) };
|
||||||
QString id = perms->getID();
|
QString id = perms->getID();
|
||||||
|
@ -611,6 +614,11 @@ bool DomainServerSettingsManager::unpackPermissionsForKeypath(const QString& key
|
||||||
void DomainServerSettingsManager::unpackPermissions() {
|
void DomainServerSettingsManager::unpackPermissions() {
|
||||||
// transfer details from _configMap to _agentPermissions
|
// transfer details from _configMap to _agentPermissions
|
||||||
|
|
||||||
|
// NOTE: Defaults for standard permissions (anonymous, friends, localhost, logged-in) used
|
||||||
|
// to be set here and then immediately persisted to the config JSON file.
|
||||||
|
// They have since been moved to describe-settings.json as the default value for AGENT_STANDARD_PERMISSIONS_KEYPATH.
|
||||||
|
// In order to change the default standard permissions you must change the default value in describe-settings.json.
|
||||||
|
|
||||||
bool needPack = false;
|
bool needPack = false;
|
||||||
|
|
||||||
needPack |= unpackPermissionsForKeypath(AGENT_STANDARD_PERMISSIONS_KEYPATH, &_standardAgentPermissions);
|
needPack |= unpackPermissionsForKeypath(AGENT_STANDARD_PERMISSIONS_KEYPATH, &_standardAgentPermissions);
|
||||||
|
@ -670,57 +678,39 @@ void DomainServerSettingsManager::unpackPermissions() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// if any of the standard names are missing, add them
|
|
||||||
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) };
|
|
||||||
|
|
||||||
if (standardKey == NodePermissions::standardNameLocalhost) {
|
|
||||||
// the localhost user is granted all permissions by default
|
|
||||||
perms->setAll(true);
|
|
||||||
} else {
|
|
||||||
// anonymous, logged in, and friend users get connect permissions by default
|
|
||||||
perms->set(NodePermissions::Permission::canConnectToDomain);
|
|
||||||
perms->set(NodePermissions::Permission::canRezTemporaryCertifiedEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the permissions to the standard map
|
|
||||||
_standardAgentPermissions[standardKey] = perms;
|
|
||||||
|
|
||||||
// this will require a packing of permissions
|
|
||||||
needPack = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
needPack |= ensurePermissionsForGroupRanks();
|
needPack |= ensurePermissionsForGroupRanks();
|
||||||
|
|
||||||
if (needPack) {
|
if (needPack) {
|
||||||
packPermissions();
|
packPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
qDebug() << "--------------- permissions ---------------------";
|
qDebug() << "--------------- permissions ---------------------";
|
||||||
QList<QHash<NodePermissionsKey, NodePermissionsPointer>> permissionsSets;
|
std::list<NodePermissionsMap*> permissionsSets {
|
||||||
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get()
|
&_standardAgentPermissions, &_agentPermissions,
|
||||||
<< _groupPermissions.get() << _groupForbiddens.get()
|
&_groupPermissions, &_groupForbiddens,
|
||||||
<< _ipPermissions.get() << _macPermissions.get()
|
&_ipPermissions, &_macPermissions,
|
||||||
<< _machineFingerprintPermissions.get();
|
&_machineFingerprintPermissions
|
||||||
|
};
|
||||||
|
|
||||||
foreach (auto permissionSet, permissionsSets) {
|
foreach (auto permissionSet, permissionsSets) {
|
||||||
QHashIterator<NodePermissionsKey, NodePermissionsPointer> i(permissionSet);
|
auto& permissionKeyMap = permissionSet->get();
|
||||||
while (i.hasNext()) {
|
auto it = permissionKeyMap.begin();
|
||||||
i.next();
|
|
||||||
NodePermissionsPointer perms = i.value();
|
while (it != permissionKeyMap.end()) {
|
||||||
|
|
||||||
|
NodePermissionsPointer perms = it->second;
|
||||||
if (perms->isGroup()) {
|
if (perms->isGroup()) {
|
||||||
qDebug() << i.key() << perms->getGroupID() << perms;
|
qDebug() << it->first << perms->getGroupID() << perms;
|
||||||
} else {
|
} else {
|
||||||
qDebug() << i.key() << perms;
|
qDebug() << it->first << perms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() {
|
bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() {
|
||||||
|
@ -1068,12 +1058,22 @@ NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid&
|
||||||
return getForbiddensForGroup(groupKey.first, groupKey.second);
|
return getForbiddensForGroup(groupKey.first, groupKey.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant DomainServerSettingsManager::valueForKeyPath(const QString& keyPath) {
|
||||||
|
QReadLocker locker(&_settingsLock);
|
||||||
|
auto foundValue = _configMap.valueForKeyPath(keyPath);
|
||||||
|
return foundValue ? *foundValue : QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) {
|
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) {
|
||||||
|
QReadLocker locker(&_settingsLock);
|
||||||
const QVariant* foundValue = _configMap.valueForKeyPath(keyPath);
|
const QVariant* foundValue = _configMap.valueForKeyPath(keyPath);
|
||||||
|
|
||||||
if (foundValue) {
|
if (foundValue) {
|
||||||
return *foundValue;
|
return *foundValue;
|
||||||
} else {
|
} else {
|
||||||
|
// we don't need the settings lock anymore since we're done reading from the config map
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
int dotIndex = keyPath.indexOf('.');
|
int dotIndex = keyPath.indexOf('.');
|
||||||
|
|
||||||
QString groupKey = keyPath.mid(0, dotIndex);
|
QString groupKey = keyPath.mid(0, dotIndex);
|
||||||
|
@ -1112,9 +1112,6 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection
|
||||||
// we recurse one level deep below each group for the appropriate setting
|
// we recurse one level deep below each group for the appropriate setting
|
||||||
bool restartRequired = recurseJSONObjectAndOverwriteSettings(postedObject, endpointType);
|
bool restartRequired = recurseJSONObjectAndOverwriteSettings(postedObject, endpointType);
|
||||||
|
|
||||||
// store whatever the current _settingsMap is to file
|
|
||||||
persistToFile();
|
|
||||||
|
|
||||||
// return success to the caller
|
// return success to the caller
|
||||||
QString jsonSuccess = "{\"status\": \"success\"}";
|
QString jsonSuccess = "{\"status\": \"success\"}";
|
||||||
connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json");
|
connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json");
|
||||||
|
@ -1216,16 +1213,9 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DomainServerSettingsManager::restoreSettingsFromObject(QJsonObject settingsToRestore, SettingsType settingsType) {
|
bool DomainServerSettingsManager::restoreSettingsFromObject(QJsonObject settingsToRestore, SettingsType settingsType) {
|
||||||
|
|
||||||
if (thread() != QThread::currentThread()) {
|
|
||||||
bool success;
|
|
||||||
|
|
||||||
BLOCKING_INVOKE_METHOD(this, "restoreSettingsFromObject",
|
// grab a write lock since we're about to change the settings map
|
||||||
Q_RETURN_ARG(bool, success),
|
QWriteLocker locker(&_settingsLock);
|
||||||
Q_ARG(QJsonObject, settingsToRestore),
|
|
||||||
Q_ARG(SettingsType, settingsType));
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonArray* filteredDescriptionArray = settingsType == DomainSettings
|
QJsonArray* filteredDescriptionArray = settingsType == DomainSettings
|
||||||
? &_domainSettingsDescription : &_contentSettingsDescription;
|
? &_domainSettingsDescription : &_contentSettingsDescription;
|
||||||
|
@ -1341,6 +1331,10 @@ bool DomainServerSettingsManager::restoreSettingsFromObject(QJsonObject settings
|
||||||
} else {
|
} else {
|
||||||
// restore completed, persist the new settings
|
// restore completed, persist the new settings
|
||||||
qDebug() << "Restore completed, persisting restored settings to file";
|
qDebug() << "Restore completed, persisting restored settings to file";
|
||||||
|
|
||||||
|
// let go of the write lock since we're done making changes to the config map
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
persistToFile();
|
persistToFile();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1352,20 +1346,6 @@ QJsonObject DomainServerSettingsManager::settingsResponseObjectForType(const QSt
|
||||||
bool includeDefaults, bool isForBackup) {
|
bool includeDefaults, bool isForBackup) {
|
||||||
QJsonObject responseObject;
|
QJsonObject responseObject;
|
||||||
|
|
||||||
if (thread() != QThread::currentThread()) {
|
|
||||||
|
|
||||||
BLOCKING_INVOKE_METHOD(this, "settingsResponseObjectForType",
|
|
||||||
Q_RETURN_ARG(QJsonObject, responseObject),
|
|
||||||
Q_ARG(const QString&, typeValue),
|
|
||||||
Q_ARG(bool, isAuthenticated),
|
|
||||||
Q_ARG(bool, includeDomainSettings),
|
|
||||||
Q_ARG(bool, includeContentSettings),
|
|
||||||
Q_ARG(bool, includeDefaults),
|
|
||||||
Q_ARG(bool, isForBackup));
|
|
||||||
|
|
||||||
return responseObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!typeValue.isEmpty() || isAuthenticated) {
|
if (!typeValue.isEmpty() || isAuthenticated) {
|
||||||
// convert the string type value to a QJsonValue
|
// convert the string type value to a QJsonValue
|
||||||
QJsonValue queryType = typeValue.isEmpty() ? QJsonValue() : QJsonValue(typeValue.toInt());
|
QJsonValue queryType = typeValue.isEmpty() ? QJsonValue() : QJsonValue(typeValue.toInt());
|
||||||
|
@ -1374,6 +1354,7 @@ QJsonObject DomainServerSettingsManager::settingsResponseObjectForType(const QSt
|
||||||
|
|
||||||
// only enumerate the requested settings type (domain setting or content setting)
|
// only enumerate the requested settings type (domain setting or content setting)
|
||||||
QJsonArray* filteredDescriptionArray = &_descriptionArray;
|
QJsonArray* filteredDescriptionArray = &_descriptionArray;
|
||||||
|
|
||||||
if (includeDomainSettings && !includeContentSettings) {
|
if (includeDomainSettings && !includeContentSettings) {
|
||||||
filteredDescriptionArray = &_domainSettingsDescription;
|
filteredDescriptionArray = &_domainSettingsDescription;
|
||||||
} else if (includeContentSettings && !includeDomainSettings) {
|
} else if (includeContentSettings && !includeDomainSettings) {
|
||||||
|
@ -1413,21 +1394,21 @@ QJsonObject DomainServerSettingsManager::settingsResponseObjectForType(const QSt
|
||||||
QVariant variantValue;
|
QVariant variantValue;
|
||||||
|
|
||||||
if (!groupKey.isEmpty()) {
|
if (!groupKey.isEmpty()) {
|
||||||
QVariant settingsMapGroupValue = _configMap.value(groupKey);
|
QVariant settingsMapGroupValue = valueForKeyPath(groupKey);
|
||||||
|
|
||||||
if (!settingsMapGroupValue.isNull()) {
|
if (!settingsMapGroupValue.isNull()) {
|
||||||
variantValue = settingsMapGroupValue.toMap().value(settingName);
|
variantValue = settingsMapGroupValue.toMap().value(settingName);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
variantValue = _configMap.value(settingName);
|
variantValue = valueForKeyPath(settingName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// final check for inclusion
|
// final check for inclusion
|
||||||
// either we include default values or we don't but this isn't a default value
|
// either we include default values or we don't but this isn't a default value
|
||||||
if (includeDefaults || !variantValue.isNull()) {
|
if (includeDefaults || variantValue.isValid()) {
|
||||||
QJsonValue result;
|
QJsonValue result;
|
||||||
|
|
||||||
if (variantValue.isNull()) {
|
if (!variantValue.isValid()) {
|
||||||
// no value for this setting, pass the default
|
// no value for this setting, pass the default
|
||||||
if (settingObject.contains(SETTING_DEFAULT_KEY)) {
|
if (settingObject.contains(SETTING_DEFAULT_KEY)) {
|
||||||
result = settingObject[SETTING_DEFAULT_KEY];
|
result = settingObject[SETTING_DEFAULT_KEY];
|
||||||
|
@ -1566,6 +1547,10 @@ QJsonObject DomainServerSettingsManager::settingDescriptionFromGroup(const QJson
|
||||||
|
|
||||||
bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject,
|
bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject,
|
||||||
SettingsType settingsType) {
|
SettingsType settingsType) {
|
||||||
|
|
||||||
|
// take a write lock since we're about to overwrite settings in the config map
|
||||||
|
QWriteLocker locker(&_settingsLock);
|
||||||
|
|
||||||
static const QString SECURITY_ROOT_KEY = "security";
|
static const QString SECURITY_ROOT_KEY = "security";
|
||||||
static const QString AC_SUBNET_WHITELIST_KEY = "ac_subnet_whitelist";
|
static const QString AC_SUBNET_WHITELIST_KEY = "ac_subnet_whitelist";
|
||||||
static const QString BROADCASTING_KEY = "broadcasting";
|
static const QString BROADCASTING_KEY = "broadcasting";
|
||||||
|
@ -1663,6 +1648,12 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we're done making changes to the config map, let go of our read lock
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
|
// store whatever the current config map is to file
|
||||||
|
persistToFile();
|
||||||
|
|
||||||
return needRestart;
|
return needRestart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1689,6 +1680,9 @@ bool permissionVariantLessThan(const QVariant &v1, const QVariant &v2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServerSettingsManager::sortPermissions() {
|
void DomainServerSettingsManager::sortPermissions() {
|
||||||
|
// take a write lock since we're about to change the config map data
|
||||||
|
QWriteLocker locker(&_settingsLock);
|
||||||
|
|
||||||
// sort the permission-names
|
// sort the permission-names
|
||||||
QVariant* standardPermissions = _configMap.valueForKeyPath(AGENT_STANDARD_PERMISSIONS_KEYPATH);
|
QVariant* standardPermissions = _configMap.valueForKeyPath(AGENT_STANDARD_PERMISSIONS_KEYPATH);
|
||||||
if (standardPermissions && standardPermissions->canConvert(QMetaType::QVariantList)) {
|
if (standardPermissions && standardPermissions->canConvert(QMetaType::QVariantList)) {
|
||||||
|
@ -1725,11 +1719,15 @@ void DomainServerSettingsManager::persistToFile() {
|
||||||
QFile settingsFile(_configMap.getUserConfigFilename());
|
QFile settingsFile(_configMap.getUserConfigFilename());
|
||||||
|
|
||||||
if (settingsFile.open(QIODevice::WriteOnly)) {
|
if (settingsFile.open(QIODevice::WriteOnly)) {
|
||||||
|
// take a read lock so we can grab the config and write it to file
|
||||||
|
QReadLocker locker(&_settingsLock);
|
||||||
settingsFile.write(QJsonDocument::fromVariant(_configMap.getConfig()).toJson());
|
settingsFile.write(QJsonDocument::fromVariant(_configMap.getConfig()).toJson());
|
||||||
} else {
|
} else {
|
||||||
qCritical("Could not write to JSON settings file. Unable to persist settings.");
|
qCritical("Could not write to JSON settings file. Unable to persist settings.");
|
||||||
|
|
||||||
// failed to write, reload whatever the current config state is
|
// failed to write, reload whatever the current config state is
|
||||||
|
// with a write lock since we're about to overwrite the config map
|
||||||
|
QWriteLocker locker(&_settingsLock);
|
||||||
_configMap.loadConfig(_argumentList);
|
_configMap.loadConfig(_argumentList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,6 @@
|
||||||
|
|
||||||
const QString SETTINGS_PATHS_KEY = "paths";
|
const QString SETTINGS_PATHS_KEY = "paths";
|
||||||
|
|
||||||
const QString SETTINGS_PATH = "/settings";
|
|
||||||
const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json";
|
|
||||||
const QString CONTENT_SETTINGS_PATH_JSON = "/content-settings.json";
|
|
||||||
const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permissions";
|
const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permissions";
|
||||||
const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions";
|
const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions";
|
||||||
const QString IP_PERMISSIONS_KEYPATH = "security.ip_permissions";
|
const QString IP_PERMISSIONS_KEYPATH = "security.ip_permissions";
|
||||||
|
@ -53,11 +50,12 @@ public:
|
||||||
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||||
|
|
||||||
void setupConfigMap(const QStringList& argumentList);
|
void setupConfigMap(const QStringList& argumentList);
|
||||||
|
|
||||||
|
// each of the three methods in this group takes a read lock of _settingsLock
|
||||||
|
// and cannot be called when the a write lock is held by the same thread
|
||||||
QVariant valueOrDefaultValueForKeyPath(const QString& keyPath);
|
QVariant valueOrDefaultValueForKeyPath(const QString& keyPath);
|
||||||
|
QVariant valueForKeyPath(const QString& keyPath);
|
||||||
QVariantMap& getSettingsMap() { return _configMap.getConfig(); }
|
bool containsKeyPath(const QString& keyPath) { return valueForKeyPath(keyPath).isValid(); }
|
||||||
|
|
||||||
QVariantMap& getDescriptorsMap();
|
|
||||||
|
|
||||||
// these give access to anonymous/localhost/logged-in settings from the domain-server settings page
|
// these give access to anonymous/localhost/logged-in settings from the domain-server settings page
|
||||||
bool haveStandardPermissionsForName(const QString& name) const { return _standardAgentPermissions.contains(name, 0); }
|
bool haveStandardPermissionsForName(const QString& name) const { return _standardAgentPermissions.contains(name, 0); }
|
||||||
|
@ -119,6 +117,8 @@ public:
|
||||||
/// thread safe method to restore settings from a JSON object
|
/// thread safe method to restore settings from a JSON object
|
||||||
Q_INVOKABLE bool restoreSettingsFromObject(QJsonObject settingsToRestore, SettingsType settingsType);
|
Q_INVOKABLE bool restoreSettingsFromObject(QJsonObject settingsToRestore, SettingsType settingsType);
|
||||||
|
|
||||||
|
bool recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, SettingsType settingsType);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateNodePermissions();
|
void updateNodePermissions();
|
||||||
void settingsUpdated();
|
void settingsUpdated();
|
||||||
|
@ -138,12 +138,13 @@ private:
|
||||||
QStringList _argumentList;
|
QStringList _argumentList;
|
||||||
|
|
||||||
QJsonArray filteredDescriptionArray(bool isContentSettings);
|
QJsonArray filteredDescriptionArray(bool isContentSettings);
|
||||||
bool recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, SettingsType settingsType);
|
|
||||||
|
|
||||||
void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
|
void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
|
||||||
const QJsonObject& settingDescription);
|
const QJsonObject& settingDescription);
|
||||||
QJsonObject settingDescriptionFromGroup(const QJsonObject& groupObject, const QString& settingName);
|
QJsonObject settingDescriptionFromGroup(const QJsonObject& groupObject, const QString& settingName);
|
||||||
void sortPermissions();
|
void sortPermissions();
|
||||||
|
|
||||||
|
// you cannot be holding the _settingsLock when persisting to file from the same thread
|
||||||
|
// since it may take either a read lock or write lock and recursive locking doesn't allow a change in type
|
||||||
void persistToFile();
|
void persistToFile();
|
||||||
|
|
||||||
void splitSettingsDescription();
|
void splitSettingsDescription();
|
||||||
|
@ -155,10 +156,10 @@ private:
|
||||||
QJsonArray _contentSettingsDescription;
|
QJsonArray _contentSettingsDescription;
|
||||||
QJsonObject _settingsMenuGroups;
|
QJsonObject _settingsMenuGroups;
|
||||||
|
|
||||||
|
// any method that calls _valueForKeyPath on this _configMap must get a write lock it keeps until it
|
||||||
|
// is done with the returned QVariant*
|
||||||
HifiConfigVariantMap _configMap;
|
HifiConfigVariantMap _configMap;
|
||||||
|
|
||||||
friend class DomainServer;
|
|
||||||
|
|
||||||
// these cause calls to metaverse's group api
|
// these cause calls to metaverse's group api
|
||||||
void apiGetGroupID(const QString& groupName);
|
void apiGetGroupID(const QString& groupName);
|
||||||
void apiGetGroupRanks(const QUuid& groupID);
|
void apiGetGroupRanks(const QUuid& groupID);
|
||||||
|
@ -192,6 +193,9 @@ private:
|
||||||
|
|
||||||
// keep track of answers to api queries about which users are in which groups
|
// keep track of answers to api queries about which users are in which groups
|
||||||
QHash<QString, QHash<QUuid, QUuid>> _groupMembership; // QHash<user-name, QHash<group-id, rank-id>>
|
QHash<QString, QHash<QUuid, QUuid>> _groupMembership; // QHash<user-name, QHash<group-id, rank-id>>
|
||||||
|
|
||||||
|
/// guard read/write access from multiple threads to settings
|
||||||
|
QReadWriteLock _settingsLock { QReadWriteLock::Recursive };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_DomainServerSettingsManager_h
|
#endif // hifi_DomainServerSettingsManager_h
|
||||||
|
|
Loading…
Reference in a new issue