mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 15:30:38 +02:00
Merge pull request #4921 from birarda/domain-toggle
add a global toggle for restricted users
This commit is contained in:
commit
1b0ef406ed
14 changed files with 731 additions and 599 deletions
|
@ -1,4 +1,6 @@
|
|||
[
|
||||
{
|
||||
"version": 1.0,
|
||||
"settings": [
|
||||
{
|
||||
"name": "metaverse",
|
||||
"label": "Metaverse / Networking",
|
||||
|
@ -85,11 +87,18 @@
|
|||
"help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.",
|
||||
"value-hidden": true
|
||||
},
|
||||
{
|
||||
"name": "restricted_access",
|
||||
"type": "checkbox",
|
||||
"label": "Restricted Access",
|
||||
"default": false,
|
||||
"help": "Only users listed in \"Allowed Users\" can enter your domain."
|
||||
},
|
||||
{
|
||||
"name": "allowed_users",
|
||||
"type": "table",
|
||||
"label": "Allowed Users",
|
||||
"help": "List the High Fidelity names for people you want to be able to connect to this domain.<br/>An empty list means everyone.<br/>You can always connect from the domain-server machine.",
|
||||
"help": "You can always connect from the domain-server machine.",
|
||||
"numbered": false,
|
||||
"columns": [
|
||||
{
|
||||
|
@ -124,8 +133,8 @@
|
|||
{
|
||||
"name": "editors_are_rezzers",
|
||||
"type": "checkbox",
|
||||
"label": "Only editors can create new entities",
|
||||
"help": "When checked, only those who can edit the domain can create new entites.",
|
||||
"label": "Only Editors Can Create Entities",
|
||||
"help": "Only users listed in \"Allowed Editors\" can create new entites.",
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
|
@ -180,8 +189,9 @@
|
|||
},
|
||||
{
|
||||
"name": "enable_filter",
|
||||
"label": "Low-pass Filter",
|
||||
"type": "checkbox",
|
||||
"help": "positional audio stream uses lowpass filter",
|
||||
"help": "Positional audio stream uses low-pass filter",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
|
@ -282,7 +292,7 @@
|
|||
"name": "dynamic_jitter_buffer",
|
||||
"type": "checkbox",
|
||||
"label": "Dynamic Jitter Buffers",
|
||||
"help": "dynamically buffer client audio based on perceived jitter in packet receipt timing",
|
||||
"help": "Dynamically buffer client audio based on perceived jitter in packet receipt timing",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
|
@ -305,8 +315,8 @@
|
|||
{
|
||||
"name": "use_stdev_for_desired_calc",
|
||||
"type": "checkbox",
|
||||
"label": "Use Stdev for Desired Jitter Frames Calc:",
|
||||
"help": "use Philip's method (stdev of timegaps) to calculate desired jitter frames (otherwise Fred's max timegap method is used)",
|
||||
"label": "Stdev for Desired Jitter Frames Calc",
|
||||
"help": "Use Philip's method (stdev of timegaps) to calculate desired jitter frames (otherwise Fred's max timegap method is used)",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
|
@ -320,7 +330,7 @@
|
|||
},
|
||||
{
|
||||
"name": "window_seconds_for_desired_calc_on_too_many_starves",
|
||||
"label": "Timegaps Window (A) Seconds:",
|
||||
"label": "Timegaps Window (A) Seconds",
|
||||
"help": "Window A contains a history of timegaps. Its max timegap is used to re-evaluate the desired jitter frames when too many starves occur within it.",
|
||||
"placeholder": "50",
|
||||
"default": "50",
|
||||
|
@ -328,7 +338,7 @@
|
|||
},
|
||||
{
|
||||
"name": "window_seconds_for_desired_reduction",
|
||||
"label": "Timegaps Window (B) Seconds:",
|
||||
"label": "Timegaps Window (B) Seconds",
|
||||
"help": "Window B contains a history of timegaps. Its max timegap is used as a ceiling for the desired jitter frames value.",
|
||||
"placeholder": "10",
|
||||
"default": "10",
|
||||
|
@ -337,16 +347,16 @@
|
|||
{
|
||||
"name": "repetition_with_fade",
|
||||
"type": "checkbox",
|
||||
"label": "Repetition with Fade:",
|
||||
"help": "dropped frames and mixing during starves repeat the last frame, eventually fading to silence",
|
||||
"label": "Repetition with Fade",
|
||||
"help": "Dropped frames and mixing during starves repeat the last frame, eventually fading to silence",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "print_stream_stats",
|
||||
"type": "checkbox",
|
||||
"label": "Print Stream Stats:",
|
||||
"help": "audio upstream and downstream stats of each agent printed to audio-mixer stdout",
|
||||
"label": "Print Stream Stats",
|
||||
"help": "Audio upstream and downstream stats of each agent printed to audio-mixer stdout",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
}
|
||||
|
@ -440,6 +450,7 @@
|
|||
{
|
||||
"name": "NoPersist",
|
||||
"type": "checkbox",
|
||||
"label": "Disable Persistence",
|
||||
"help": "Don't persist your entities to a file.",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
|
@ -447,6 +458,7 @@
|
|||
{
|
||||
"name": "NoBackup",
|
||||
"type": "checkbox",
|
||||
"label": "Disable Backup",
|
||||
"help": "Don't regularly backup your persisted entities to a backup file.",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
|
@ -454,7 +466,7 @@
|
|||
{
|
||||
"name": "statusHost",
|
||||
"label": "Status Hostname",
|
||||
"help": "host name or IP address of the server for accessing the status page",
|
||||
"help": "Host name or IP address of the server for accessing the status page",
|
||||
"placeholder": "",
|
||||
"default": "",
|
||||
"advanced": true
|
||||
|
@ -462,7 +474,7 @@
|
|||
{
|
||||
"name": "statusPort",
|
||||
"label": "Status Port",
|
||||
"help": "port of the server for accessing the status page",
|
||||
"help": "Port of the server for accessing the status page",
|
||||
"placeholder": "",
|
||||
"default": "",
|
||||
"advanced": true
|
||||
|
@ -470,6 +482,7 @@
|
|||
{
|
||||
"name": "wantEditLogging",
|
||||
"type": "checkbox",
|
||||
"label": "Edit Logging",
|
||||
"help": "Logging of all edits to entities",
|
||||
"default": true,
|
||||
"advanced": true
|
||||
|
@ -477,28 +490,32 @@
|
|||
{
|
||||
"name": "verboseDebug",
|
||||
"type": "checkbox",
|
||||
"help": "lots of debugging",
|
||||
"label": "Verbose Debug",
|
||||
"help": "Lots of debugging",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "debugReceiving",
|
||||
"type": "checkbox",
|
||||
"help": "extra debugging on receiving",
|
||||
"label": "Extra Receiving Debug",
|
||||
"help": "Extra debugging on receiving",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "debugSending",
|
||||
"type": "checkbox",
|
||||
"help": "extra debugging on sending",
|
||||
"label": "Extra Sending Debug",
|
||||
"help": "Extra debugging on sending",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "debugTimestampNow",
|
||||
"type": "checkbox",
|
||||
"help": "extra debugging for usecTimestampNow() function",
|
||||
"label": "Extra Timestamp Debugging",
|
||||
"help": "Extra debugging for usecTimestampNow() function",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
|
@ -529,3 +546,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
22
domain-server/resources/web/css/bootstrap-switch.min.css
vendored
Executable file
22
domain-server/resources/web/css/bootstrap-switch.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
|
@ -109,6 +109,13 @@ table {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
/* styling for bootstrap-switch toggles */
|
||||
.checkbox-help {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* CSS only spinner for AJAX requests */
|
||||
|
||||
.spinner {
|
||||
margin: 30px auto 0;
|
||||
width: 70px;
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
<link href="/css/bootstrap.min.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/style.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/sweetalert.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/bootstrap-switch.min.css" rel="stylesheet" media="screen">
|
||||
<link href="/stats/css/json.human.css" rel="stylesheet" media="screen">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
|
|
|
@ -99,7 +99,8 @@
|
|||
<script src='/js/underscore-min.js'></script>
|
||||
<script src='/js/underscore-keypath.min.js'></script>
|
||||
<script src='/js/bootbox.min.js'></script>
|
||||
<script src='/js/sweetalert.min.js'></script>
|
||||
<script src='/js/settings.js'></script>
|
||||
<script src='/js/form2js.min.js'></script>
|
||||
<script src='js/bootstrap-switch.min.js'></script>
|
||||
<script src='js/sweetalert.min.js'></script>
|
||||
<script src='js/settings.js'></script>
|
||||
<script src='js/form2js.min.js'></script>
|
||||
<!--#include virtual="page-end.html"-->
|
||||
|
|
22
domain-server/resources/web/settings/js/bootstrap-switch.min.js
vendored
Executable file
22
domain-server/resources/web/settings/js/bootstrap-switch.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
|
@ -60,10 +60,15 @@ var viewHelpers = {
|
|||
if (setting.label) {
|
||||
form_group += "<label class='" + label_class + "'>" + setting.label + "</label>"
|
||||
}
|
||||
form_group += "<div class='checkbox" + (isLocked ? " disabled" : "") + "'>"
|
||||
form_group += "<label for='" + keypath + "'>"
|
||||
form_group += "<input type='checkbox'" + common_attrs() + (setting_value ? "checked" : "") + (isLocked ? " disabled" : "") + "/>"
|
||||
form_group += " " + setting.help + "</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" : "") + "/>"
|
||||
|
||||
if (setting.help) {
|
||||
form_group += "<span class='help-block checkbox-help'>" + setting.help + "</span>";
|
||||
}
|
||||
|
||||
form_group += "</div>"
|
||||
} else {
|
||||
input_type = _.has(setting, 'type') ? setting.type : "text"
|
||||
|
@ -201,10 +206,17 @@ $(document).ready(function(){
|
|||
|
||||
$('#' + Settings.FORM_ID).on('change', '.' + Settings.TRIGGER_CHANGE_CLASS , function(){
|
||||
// this input was changed, add the changed data attribute to it
|
||||
$(this).attr('data-changed', true)
|
||||
$(this).attr('data-changed', true);
|
||||
|
||||
badgeSidebarForDifferences($(this))
|
||||
})
|
||||
badgeSidebarForDifferences($(this));
|
||||
});
|
||||
|
||||
$('#' + Settings.FORM_ID).on('switchChange.bootstrapSwitch', 'input.toggle-checkbox', function(){
|
||||
// this checkbox was changed, add the changed data attribute to it
|
||||
$(this).attr('data-changed', true);
|
||||
|
||||
badgeSidebarForDifferences($(this));
|
||||
});
|
||||
|
||||
$('.advanced-toggle').click(function(){
|
||||
Settings.showAdvanced = !Settings.showAdvanced
|
||||
|
@ -735,6 +747,9 @@ function reloadSettings() {
|
|||
// call our method to setup the place names table
|
||||
setupPlacesTable();
|
||||
|
||||
// setup any bootstrap switches
|
||||
$('.toggle-checkbox').bootstrapSwitch();
|
||||
|
||||
// add tooltip to locked settings
|
||||
$('label.locked').tooltip({
|
||||
placement: 'right',
|
|
@ -44,13 +44,10 @@ int const DomainServer::EXIT_CODE_REBOOT = 234923;
|
|||
|
||||
const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io";
|
||||
|
||||
|
||||
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
|
||||
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
|
||||
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
|
||||
const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers";
|
||||
|
||||
|
||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||
|
@ -776,9 +773,8 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
|||
const HifiSockAddr& senderSockAddr,
|
||||
QString& reasonReturn) {
|
||||
|
||||
const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(),
|
||||
ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
|
||||
bool isRestrictingAccess =
|
||||
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
|
||||
|
||||
// we always let in a user who is sending a packet from our local socket or from the localhost address
|
||||
if (senderSockAddr.getAddress() == DependencyManager::get<LimitedNodeList>()->getLocalSockAddr().getAddress()
|
||||
|
@ -786,18 +782,24 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (allowedUsers.count() > 0) {
|
||||
if (isRestrictingAccess) {
|
||||
|
||||
QStringList allowedUsers =
|
||||
_settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList();
|
||||
|
||||
if (allowedUsers.contains(username, Qt::CaseInsensitive)) {
|
||||
if (verifyUsersKey(username, usernameSignature, reasonReturn)) {
|
||||
return true;
|
||||
if (!verifyUsersKey(username, usernameSignature, reasonReturn)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Connect request denied for user" << username << "not in allowed users list.";
|
||||
reasonReturn = "User not on whitelist.";
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
// we have no allowed user list.
|
||||
}
|
||||
}
|
||||
|
||||
// either we aren't restricting users, or this user is in the allowed list
|
||||
|
||||
// if this user is in the editors list, exempt them from the max-capacity check
|
||||
const QVariant* allowedEditorsVariant =
|
||||
|
@ -825,7 +827,6 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
|||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::preloadAllowedUserPublicKeys() {
|
||||
const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||
|
@ -1255,10 +1256,8 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) {
|
|||
// add a flag to indicate if this domain uses restricted access - for now that will exclude it from listings
|
||||
const QString RESTRICTED_ACCESS_FLAG = "restricted";
|
||||
|
||||
const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(),
|
||||
ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
|
||||
domainObject[RESTRICTED_ACCESS_FLAG] = (allowedUsers.size() > 0);
|
||||
domainObject[RESTRICTED_ACCESS_FLAG] =
|
||||
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
|
||||
|
||||
// add the number of currently connected agent users
|
||||
int numConnectedAuthedUsers = 0;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QtCore/QFile>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QUrlQuery>
|
||||
|
@ -42,34 +43,74 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
|
|||
QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH);
|
||||
descriptionFile.open(QIODevice::ReadOnly);
|
||||
|
||||
_descriptionArray = QJsonDocument::fromJson(descriptionFile.readAll()).array();
|
||||
QJsonDocument descriptionDocument = QJsonDocument::fromJson(descriptionFile.readAll());
|
||||
|
||||
if (descriptionDocument.isObject()) {
|
||||
QJsonObject descriptionObject = descriptionDocument.object();
|
||||
|
||||
const QString DESCRIPTION_VERSION_KEY = "version";
|
||||
|
||||
if (descriptionObject.contains(DESCRIPTION_VERSION_KEY)) {
|
||||
// read the version from the settings description
|
||||
_descriptionVersion = descriptionObject[DESCRIPTION_VERSION_KEY].toDouble();
|
||||
|
||||
if (descriptionObject.contains(DESCRIPTION_SETTINGS_KEY)) {
|
||||
_descriptionArray = descriptionDocument.object()[DESCRIPTION_SETTINGS_KEY].toArray();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qCritical() << "Did not find settings decription in JSON at" << SETTINGS_DESCRIPTION_RELATIVE_PATH
|
||||
<< "- Unable to continue. domain-server will quit.";
|
||||
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||
_configMap.loadMasterAndUserConfig(argumentList);
|
||||
|
||||
// for now we perform a temporary transition from http-username and http-password to http_username and http_password
|
||||
const QVariant* oldUsername = valueForKeyPath(_configMap.getUserConfig(), "security.http-username");
|
||||
const QVariant* oldPassword = valueForKeyPath(_configMap.getUserConfig(), "security.http-password");
|
||||
// What settings version were we before and what are we using now?
|
||||
// Do we need to do any re-mapping?
|
||||
QSettings appSettings;
|
||||
const QString JSON_SETTINGS_VERSION_KEY = "json-settings/version";
|
||||
double oldVersion = appSettings.value(JSON_SETTINGS_VERSION_KEY, 0.0).toDouble();
|
||||
|
||||
if (oldUsername || oldPassword) {
|
||||
QVariantMap& settingsMap = *reinterpret_cast<QVariantMap*>(_configMap.getUserConfig()["security"].data());
|
||||
if (oldVersion != _descriptionVersion) {
|
||||
qDebug() << "Previous domain-server settings version was"
|
||||
<< QString::number(oldVersion, 'g', 8) << "and the new version is"
|
||||
<< QString::number(_descriptionVersion, 'g', 8) << "- checking if any re-mapping is required";
|
||||
|
||||
// remove old keys, move to new format
|
||||
if (oldUsername) {
|
||||
settingsMap["http_username"] = oldUsername->toString();
|
||||
settingsMap.remove("http-username");
|
||||
}
|
||||
// we have a version mismatch - for now handle custom behaviour here since there are not many remappings
|
||||
if (oldVersion < 1.0) {
|
||||
// This was prior to the introduction of security.restricted_access
|
||||
// If the user has a list of allowed users then set their value for security.restricted_access to true
|
||||
|
||||
if (oldPassword) {
|
||||
settingsMap["http_password"] = oldPassword->toString();
|
||||
settingsMap.remove("http-password");
|
||||
}
|
||||
QVariant* allowedUsers = valueForKeyPath(_configMap.getMergedConfig(), ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||
|
||||
// save the updated settings
|
||||
if (allowedUsers
|
||||
&& allowedUsers->canConvert(QMetaType::QVariantList)
|
||||
&& reinterpret_cast<QVariantList*>(allowedUsers)->size() > 0) {
|
||||
|
||||
qDebug() << "Forcing security.restricted_access to TRUE since there was an"
|
||||
<< "existing list of allowed users.";
|
||||
|
||||
// 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);
|
||||
|
||||
*restrictedAccess = QVariant(true);
|
||||
|
||||
// write the new settings to the json file
|
||||
persistToFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write the current description version to our settings
|
||||
appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion);
|
||||
}
|
||||
|
||||
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString &keyPath) {
|
||||
const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath);
|
||||
|
|
|
@ -23,6 +23,9 @@ const QString SETTINGS_PATHS_KEY = "paths";
|
|||
const QString SETTINGS_PATH = "/settings";
|
||||
const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json";
|
||||
|
||||
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
|
||||
const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access";
|
||||
|
||||
class DomainServerSettingsManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -44,6 +47,7 @@ private:
|
|||
QJsonObject settingDescriptionFromGroup(const QJsonObject& groupObject, const QString& settingName);
|
||||
void persistToFile();
|
||||
|
||||
double _descriptionVersion;
|
||||
QJsonArray _descriptionArray;
|
||||
HifiConfigVariantMap _configMap;
|
||||
};
|
||||
|
|
|
@ -159,16 +159,17 @@ void HifiConfigVariantMap::addMissingValuesToExistingMap(QVariantMap& existingMa
|
|||
}
|
||||
}
|
||||
|
||||
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath) {
|
||||
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath, bool shouldCreateIfMissing) {
|
||||
int dotIndex = keyPath.indexOf('.');
|
||||
|
||||
QString firstKey = (dotIndex == -1) ? keyPath : keyPath.mid(0, dotIndex);
|
||||
|
||||
if (variantMap.contains(firstKey)) {
|
||||
if (shouldCreateIfMissing || variantMap.contains(firstKey)) {
|
||||
if (dotIndex == -1) {
|
||||
return &variantMap[firstKey];
|
||||
} else if (variantMap[firstKey].canConvert(QMetaType::QVariantMap)) {
|
||||
return valueForKeyPath(*static_cast<QVariantMap*>(variantMap[firstKey].data()), keyPath.mid(dotIndex + 1));
|
||||
return valueForKeyPath(*static_cast<QVariantMap*>(variantMap[firstKey].data()), keyPath.mid(dotIndex + 1),
|
||||
shouldCreateIfMissing);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,6 @@ private:
|
|||
void addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap);
|
||||
};
|
||||
|
||||
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath);
|
||||
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath, bool shouldCreateIfMissing = false);
|
||||
|
||||
#endif // hifi_HifiConfigVariantMap_h
|
||||
|
|
Loading…
Reference in a new issue