//// DomainServerSettingsManager.cpp// domain-server/src//// Created by Stephen Birarda on 2014-06-24.// Copyright 2014 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include <algorithm> #include <QtCore/QCoreApplication>#include <QtCore/QDir>#include <QtCore/QFile>#include <QtCore/QJsonArray>#include <QtCore/QJsonObject>#include <QtCore/QSettings>#include <QtCore/QStandardPaths>#include <QtCore/QUrl>#include <QtCore/QUrlQuery> #include <QTimeZone> #include <Assignment.h>#include <HifiConfigVariantMap.h>#include <HTTPConnection.h>#include <NLPacketList.h> #include "DomainServerSettingsManager.h" #define WANT_DEBUG 1 const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json"; const QString DESCRIPTION_SETTINGS_KEY = "settings";const QString SETTING_DEFAULT_KEY = "default";const QString DESCRIPTION_NAME_KEY = "name";const QString SETTING_DESCRIPTION_TYPE_KEY = "type";const QString DESCRIPTION_COLUMNS_KEY = "columns"; const QString SETTINGS_VIEWPOINT_KEY = "viewpoint"; DomainServerSettingsManager::DomainServerSettingsManager() : _descriptionArray(), _configMap(){ // load the description object from the settings description QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH); descriptionFile.open(QIODevice::ReadOnly); QJsonParseError parseError; QJsonDocument descriptionDocument = QJsonDocument::fromJson(descriptionFile.readAll(), &parseError); 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; } } } static const QString MISSING_SETTINGS_DESC_MSG = QString("Did not find settings description in JSON at %1 - Unable to continue. domain-server will quit.\n%2 at %3") .arg(SETTINGS_DESCRIPTION_RELATIVE_PATH).arg(parseError.errorString()).arg(parseError.offset); static const int MISSING_SETTINGS_DESC_ERROR_CODE = 6; QMetaObject::invokeMethod(QCoreApplication::instance(), "queuedQuit", Qt::QueuedConnection, Q_ARG(QString, MISSING_SETTINGS_DESC_MSG), Q_ARG(int, MISSING_SETTINGS_DESC_ERROR_CODE));} void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message) { Assignment::Type type; message->readPrimitive(&type); QJsonObject responseObject = responseObjectForType(QString::number(type)); auto json = QJsonDocument(responseObject).toJson(); auto packetList = NLPacketList::create(PacketType::DomainSettings, QByteArray(), true, true); packetList->write(json); auto nodeList = DependencyManager::get<LimitedNodeList>(); nodeList->sendPacketList(std::move(packetList), message->getSenderSockAddr());} void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) { _argumentList = argumentList; _configMap.loadMasterAndUserConfig(_argumentList); // 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 (oldVersion != _descriptionVersion) { const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users"; const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access"; const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors"; const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers"; 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"; // 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 QVariant* allowedUsers = valueForKeyPath(_configMap.getMergedConfig(), ALLOWED_USERS_SETTINGS_KEYPATH); 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(); // reload the master and user config so that the merged config is right _configMap.loadMasterAndUserConfig(_argumentList); } } if (oldVersion < 1.1) { static const QString ENTITY_SERVER_SETTINGS_KEY = "entity_server_settings"; static const QString ENTITY_FILE_NAME_KEY = "persistFilename"; static const QString ENTITY_FILE_PATH_KEYPATH = ENTITY_SERVER_SETTINGS_KEY + ".persistFilePath"; // this was prior to change of poorly named entitiesFileName to entitiesFilePath QVariant* persistFileNameVariant = valueForKeyPath(_configMap.getMergedConfig(), ENTITY_SERVER_SETTINGS_KEY + "." + ENTITY_FILE_NAME_KEY); if (persistFileNameVariant && persistFileNameVariant->canConvert(QMetaType::QString)) { QString persistFileName = persistFileNameVariant->toString(); qDebug() << "Migrating persistFilename to persistFilePath for entity-server settings"; // grab the persistFilePath option, create it if it doesn't exist QVariant* persistFilePath = valueForKeyPath(_configMap.getUserConfig(), ENTITY_FILE_PATH_KEYPATH, true); // write the migrated value *persistFilePath = persistFileName; // remove the old setting QVariant* entityServerVariant = valueForKeyPath(_configMap.getUserConfig(), ENTITY_SERVER_SETTINGS_KEY); if (entityServerVariant && entityServerVariant->canConvert(QMetaType::QVariantMap)) { QVariantMap entityServerMap = entityServerVariant->toMap(); entityServerMap.remove(ENTITY_FILE_NAME_KEY); *entityServerVariant = entityServerMap; } // write the new settings to the json file persistToFile(); // reload the master and user config so that the merged config is right _configMap.loadMasterAndUserConfig(_argumentList); } } if (oldVersion < 1.2) { // This was prior to the base64 encoding of password for HTTP Basic Authentication. // If we have a password in the previous settings file, make it base 64 static const QString BASIC_AUTH_PASSWORD_KEY_PATH { "security.http_password" }; QVariant* passwordVariant = valueForKeyPath(_configMap.getUserConfig(), BASIC_AUTH_PASSWORD_KEY_PATH); if (passwordVariant && passwordVariant->canConvert(QMetaType::QString)) { QString plaintextPassword = passwordVariant->toString(); qDebug() << "Migrating plaintext password to SHA256 hash in domain-server settings."; *passwordVariant = QCryptographicHash::hash(plaintextPassword.toUtf8(), QCryptographicHash::Sha256).toHex(); // write the new settings to file persistToFile(); // reload the master and user config so the merged config is correct _configMap.loadMasterAndUserConfig(_argumentList); } } if (oldVersion < 1.4) { // This was prior to the permissions-grid in the domain-server settings page bool isRestrictedAccess = valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); QStringList allowedUsers = valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList(); QStringList allowedEditors = valueOrDefaultValueForKeyPath(ALLOWED_EDITORS_SETTINGS_KEYPATH).toStringList(); bool onlyEditorsAreRezzers = valueOrDefaultValueForKeyPath(EDITORS_ARE_REZZERS_KEYPATH).toBool(); _standardAgentPermissions[NodePermissions::standardNameLocalhost].reset( new NodePermissions(NodePermissions::standardNameLocalhost)); _standardAgentPermissions[NodePermissions::standardNameLocalhost]->setAll(true); _standardAgentPermissions[NodePermissions::standardNameAnonymous].reset( new NodePermissions(NodePermissions::standardNameAnonymous)); _standardAgentPermissions[NodePermissions::standardNameLoggedIn].reset( new NodePermissions(NodePermissions::standardNameLoggedIn)); if (isRestrictedAccess) { // only users in allow-users list can connect _standardAgentPermissions[NodePermissions::standardNameAnonymous]->canConnectToDomain = false; _standardAgentPermissions[NodePermissions::standardNameLoggedIn]->canConnectToDomain = false; } // else anonymous and logged-in retain default of canConnectToDomain = true foreach (QString allowedUser, allowedUsers) { // even if isRestrictedAccess is false, we have to add explicit rows for these users. // defaults to canConnectToDomain = true _agentPermissions[allowedUser].reset(new NodePermissions(allowedUser)); } foreach (QString allowedEditor, allowedEditors) { if (!_agentPermissions.contains(allowedEditor)) { _agentPermissions[allowedEditor].reset(new NodePermissions(allowedEditor)); if (isRestrictedAccess) { // they can change locks, but can't connect. _agentPermissions[allowedEditor]->canConnectToDomain = false; } } _agentPermissions[allowedEditor]->canAdjustLocks = true; } QList<QHash<QString, NodePermissionsPointer>> permissionsSets; permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get(); foreach (auto permissionsSet, permissionsSets) { foreach (QString userName, permissionsSet.keys()) { if (onlyEditorsAreRezzers) { permissionsSet[userName]->canRezPermanentEntities = permissionsSet[userName]->canAdjustLocks; permissionsSet[userName]->canRezTemporaryEntities = permissionsSet[userName]->canAdjustLocks; } else { permissionsSet[userName]->canRezPermanentEntities = true; permissionsSet[userName]->canRezTemporaryEntities = true; } } } packPermissions(); _standardAgentPermissions.clear(); _agentPermissions.clear(); } if (oldVersion < 1.5) { // This was prior to operating hours, so add default hours static const QString WEEKDAY_HOURS{ "descriptors.weekday_hours" }; static const QString WEEKEND_HOURS{ "descriptors.weekend_hours" }; static const QString UTC_OFFSET{ "descriptors.utc_offset" }; QVariant* weekdayHours = valueForKeyPath(_configMap.getUserConfig(), WEEKDAY_HOURS, true); QVariant* weekendHours = valueForKeyPath(_configMap.getUserConfig(), WEEKEND_HOURS, true); QVariant* utcOffset = valueForKeyPath(_configMap.getUserConfig(), UTC_OFFSET, true); *weekdayHours = QVariantList { QVariantMap{ { "open", QVariant("00:00") }, { "close", QVariant("23:59") } } }; *weekendHours = QVariantList { QVariantMap{ { "open", QVariant("00:00") }, { "close", QVariant("23:59") } } }; *utcOffset = QVariant(QTimeZone::systemTimeZone().offsetFromUtc(QDateTime::currentDateTime()) / (float)3600); // write the new settings to file persistToFile(); // reload the master and user config so the merged config is correct _configMap.loadMasterAndUserConfig(_argumentList); } } unpackPermissions(); // write the current description version to our settings appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion);} void DomainServerSettingsManager::packPermissionsForMap(QString mapName, NodePermissionsMap& agentPermissions, QString keyPath) { QVariant* security = valueForKeyPath(_configMap.getUserConfig(), "security"); if (!security || !security->canConvert(QMetaType::QVariantMap)) { security = valueForKeyPath(_configMap.getUserConfig(), "security", true); (*security) = QVariantMap(); } // save settings for anonymous / logged-in / localhost QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath); if (!permissions || !permissions->canConvert(QMetaType::QVariantList)) { permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath, true); (*permissions) = QVariantList(); } QVariantList* permissionsList = reinterpret_cast<QVariantList*>(permissions); (*permissionsList).clear(); foreach (QString userName, agentPermissions.keys()) { *permissionsList += agentPermissions[userName]->toVariant(); }} void DomainServerSettingsManager::packPermissions() { // transfer details from _agentPermissions to _configMap packPermissionsForMap("standard_permissions", _standardAgentPermissions, AGENT_STANDARD_PERMISSIONS_KEYPATH); // save settings for specific users packPermissionsForMap("permissions", _agentPermissions, AGENT_PERMISSIONS_KEYPATH); persistToFile(); _configMap.loadMasterAndUserConfig(_argumentList);} void DomainServerSettingsManager::unpackPermissions() { // transfer details from _configMap to _agentPermissions; _standardAgentPermissions.clear(); _agentPermissions.clear(); bool foundLocalhost = false; bool foundAnonymous = false; bool foundLoggedIn = false; bool needPack = false; QVariant* standardPermissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_STANDARD_PERMISSIONS_KEYPATH); if (!standardPermissions || !standardPermissions->canConvert(QMetaType::QVariantList)) { qDebug() << "failed to extract standard permissions from settings."; standardPermissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_STANDARD_PERMISSIONS_KEYPATH, true); (*standardPermissions) = QVariantList(); } QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_PERMISSIONS_KEYPATH); if (!permissions || !permissions->canConvert(QMetaType::QVariantList)) { qDebug() << "failed to extract permissions from settings."; permissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_PERMISSIONS_KEYPATH, true); (*permissions) = QVariantList(); } QList<QVariant> standardPermissionsList = standardPermissions->toList(); foreach (QVariant permsHash, standardPermissionsList) { NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; QString id = perms->getID(); foundLocalhost |= (id == NodePermissions::standardNameLocalhost); foundAnonymous |= (id == NodePermissions::standardNameAnonymous); foundLoggedIn |= (id == NodePermissions::standardNameLoggedIn); if (_standardAgentPermissions.contains(id)) { qDebug() << "duplicate name in standard permissions table: " << id; _standardAgentPermissions[id] |= perms; needPack = true; } else { _standardAgentPermissions[id] = perms; } } QList<QVariant> permissionsList = permissions->toList(); foreach (QVariant permsHash, permissionsList) { NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; QString id = perms->getID(); if (_agentPermissions.contains(id)) { qDebug() << "duplicate name in permissions table: " << id; _agentPermissions[id] |= perms; needPack = true; } else { _agentPermissions[id] = perms; } } // if any of the standard names are missing, add them if (!foundLocalhost) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLocalhost) }; perms->setAll(true); _standardAgentPermissions[perms->getID()] = perms; needPack = true; } if (!foundAnonymous) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameAnonymous) }; _standardAgentPermissions[perms->getID()] = perms; needPack = true; } if (!foundLoggedIn) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLoggedIn) }; _standardAgentPermissions[perms->getID()] = perms; needPack = true; } if (needPack) { packPermissions(); } #ifdef WANT_DEBUG qDebug() << "--------------- permissions ---------------------"; QList<QHash<QString, NodePermissionsPointer>> permissionsSets; permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get(); foreach (auto permissionSet, permissionsSets) { QHashIterator<QString, NodePermissionsPointer> i(permissionSet); while (i.hasNext()) { i.next(); NodePermissionsPointer perms = i.value(); qDebug() << i.key() << perms; } } #endif} NodePermissions DomainServerSettingsManager::getStandardPermissionsForName(const QString& name) const { if (_standardAgentPermissions.contains(name)) { return *(_standardAgentPermissions[name].get()); } NodePermissions nullPermissions; nullPermissions.setAll(false); return nullPermissions;} NodePermissions DomainServerSettingsManager::getPermissionsForName(const QString& name) const { if (_agentPermissions.contains(name)) { return *(_agentPermissions[name].get()); } NodePermissions nullPermissions; nullPermissions.setAll(false); return nullPermissions;} QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath); if (foundValue) { return *foundValue; } else { int dotIndex = keyPath.indexOf('.'); QString groupKey = keyPath.mid(0, dotIndex); QString settingKey = keyPath.mid(dotIndex + 1); foreach(const QVariant& group, _descriptionArray.toVariantList()) { QVariantMap groupMap = group.toMap(); if (groupMap[DESCRIPTION_NAME_KEY].toString() == groupKey) { foreach(const QVariant& setting, groupMap[DESCRIPTION_SETTINGS_KEY].toList()) { QVariantMap settingMap = setting.toMap(); if (settingMap[DESCRIPTION_NAME_KEY].toString() == settingKey) { return settingMap[SETTING_DEFAULT_KEY]; } } return QVariant(); } } } return QVariant();} bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) { if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH_JSON) { // this is a GET operation for our settings // check if there is a query parameter for settings affecting a particular type of assignment const QString SETTINGS_TYPE_QUERY_KEY = "type"; QUrlQuery settingsQuery(url); QString typeValue = settingsQuery.queryItemValue(SETTINGS_TYPE_QUERY_KEY); if (!typeValue.isEmpty()) { QJsonObject responseObject = responseObjectForType(typeValue); connection->respond(HTTPConnection::StatusCode200, QJsonDocument(responseObject).toJson(), "application/json"); return true; } else { return false; } } return false;} bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection *connection, const QUrl &url) { if (connection->requestOperation() == QNetworkAccessManager::PostOperation && url.path() == SETTINGS_PATH_JSON) { // this is a POST operation to change one or more settings QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent()); QJsonObject postedObject = postedDocument.object(); qDebug() << "DomainServerSettingsManager postedObject -" << postedObject; // we recurse one level deep below each group for the appropriate setting bool restartRequired = recurseJSONObjectAndOverwriteSettings(postedObject); // store whatever the current _settingsMap is to file persistToFile(); Lines 1–500 of 852. Load more
// // DomainServerSettingsManager.cpp // domain-server/src // // Created by Stephen Birarda on 2014-06-24. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include <algorithm> #include <QtCore/QCoreApplication> #include <QtCore/QDir> #include <QtCore/QFile> #include <QtCore/QJsonArray> #include <QtCore/QJsonObject> #include <QtCore/QSettings> #include <QtCore/QStandardPaths> #include <QtCore/QUrl> #include <QtCore/QUrlQuery> #include <QTimeZone> #include <Assignment.h> #include <HifiConfigVariantMap.h> #include <HTTPConnection.h> #include <NLPacketList.h> #include "DomainServerSettingsManager.h" #define WANT_DEBUG 1 const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json"; const QString DESCRIPTION_SETTINGS_KEY = "settings"; const QString SETTING_DEFAULT_KEY = "default"; const QString DESCRIPTION_NAME_KEY = "name"; const QString SETTING_DESCRIPTION_TYPE_KEY = "type"; const QString DESCRIPTION_COLUMNS_KEY = "columns"; const QString SETTINGS_VIEWPOINT_KEY = "viewpoint"; DomainServerSettingsManager::DomainServerSettingsManager() : _descriptionArray(), _configMap() { // load the description object from the settings description QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH); descriptionFile.open(QIODevice::ReadOnly); QJsonParseError parseError; QJsonDocument descriptionDocument = QJsonDocument::fromJson(descriptionFile.readAll(), &parseError); 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; } } } static const QString MISSING_SETTINGS_DESC_MSG = QString("Did not find settings description in JSON at %1 - Unable to continue. domain-server will quit.\n%2 at %3") .arg(SETTINGS_DESCRIPTION_RELATIVE_PATH).arg(parseError.errorString()).arg(parseError.offset); static const int MISSING_SETTINGS_DESC_ERROR_CODE = 6; QMetaObject::invokeMethod(QCoreApplication::instance(), "queuedQuit", Qt::QueuedConnection, Q_ARG(QString, MISSING_SETTINGS_DESC_MSG), Q_ARG(int, MISSING_SETTINGS_DESC_ERROR_CODE)); } void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message) { Assignment::Type type; message->readPrimitive(&type); QJsonObject responseObject = responseObjectForType(QString::number(type)); auto json = QJsonDocument(responseObject).toJson(); auto packetList = NLPacketList::create(PacketType::DomainSettings, QByteArray(), true, true); packetList->write(json); auto nodeList = DependencyManager::get<LimitedNodeList>(); nodeList->sendPacketList(std::move(packetList), message->getSenderSockAddr()); } void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) { _argumentList = argumentList; _configMap.loadMasterAndUserConfig(_argumentList); // 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 (oldVersion != _descriptionVersion) { const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users"; const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access"; const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors"; const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers"; 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"; // 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 QVariant* allowedUsers = valueForKeyPath(_configMap.getMergedConfig(), ALLOWED_USERS_SETTINGS_KEYPATH); 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(); // reload the master and user config so that the merged config is right _configMap.loadMasterAndUserConfig(_argumentList); } } if (oldVersion < 1.1) { static const QString ENTITY_SERVER_SETTINGS_KEY = "entity_server_settings"; static const QString ENTITY_FILE_NAME_KEY = "persistFilename"; static const QString ENTITY_FILE_PATH_KEYPATH = ENTITY_SERVER_SETTINGS_KEY + ".persistFilePath"; // this was prior to change of poorly named entitiesFileName to entitiesFilePath QVariant* persistFileNameVariant = valueForKeyPath(_configMap.getMergedConfig(), ENTITY_SERVER_SETTINGS_KEY + "." + ENTITY_FILE_NAME_KEY); if (persistFileNameVariant && persistFileNameVariant->canConvert(QMetaType::QString)) { QString persistFileName = persistFileNameVariant->toString(); qDebug() << "Migrating persistFilename to persistFilePath for entity-server settings"; // grab the persistFilePath option, create it if it doesn't exist QVariant* persistFilePath = valueForKeyPath(_configMap.getUserConfig(), ENTITY_FILE_PATH_KEYPATH, true); // write the migrated value *persistFilePath = persistFileName; // remove the old setting QVariant* entityServerVariant = valueForKeyPath(_configMap.getUserConfig(), ENTITY_SERVER_SETTINGS_KEY); if (entityServerVariant && entityServerVariant->canConvert(QMetaType::QVariantMap)) { QVariantMap entityServerMap = entityServerVariant->toMap(); entityServerMap.remove(ENTITY_FILE_NAME_KEY); *entityServerVariant = entityServerMap; } // write the new settings to the json file persistToFile(); // reload the master and user config so that the merged config is right _configMap.loadMasterAndUserConfig(_argumentList); } } if (oldVersion < 1.2) { // This was prior to the base64 encoding of password for HTTP Basic Authentication. // If we have a password in the previous settings file, make it base 64 static const QString BASIC_AUTH_PASSWORD_KEY_PATH { "security.http_password" }; QVariant* passwordVariant = valueForKeyPath(_configMap.getUserConfig(), BASIC_AUTH_PASSWORD_KEY_PATH); if (passwordVariant && passwordVariant->canConvert(QMetaType::QString)) { QString plaintextPassword = passwordVariant->toString(); qDebug() << "Migrating plaintext password to SHA256 hash in domain-server settings."; *passwordVariant = QCryptographicHash::hash(plaintextPassword.toUtf8(), QCryptographicHash::Sha256).toHex(); // write the new settings to file persistToFile(); // reload the master and user config so the merged config is correct _configMap.loadMasterAndUserConfig(_argumentList); } } if (oldVersion < 1.4) { // This was prior to the permissions-grid in the domain-server settings page bool isRestrictedAccess = valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); QStringList allowedUsers = valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList(); QStringList allowedEditors = valueOrDefaultValueForKeyPath(ALLOWED_EDITORS_SETTINGS_KEYPATH).toStringList(); bool onlyEditorsAreRezzers = valueOrDefaultValueForKeyPath(EDITORS_ARE_REZZERS_KEYPATH).toBool(); _standardAgentPermissions[NodePermissions::standardNameLocalhost].reset( new NodePermissions(NodePermissions::standardNameLocalhost)); _standardAgentPermissions[NodePermissions::standardNameLocalhost]->setAll(true); _standardAgentPermissions[NodePermissions::standardNameAnonymous].reset( new NodePermissions(NodePermissions::standardNameAnonymous)); _standardAgentPermissions[NodePermissions::standardNameLoggedIn].reset( new NodePermissions(NodePermissions::standardNameLoggedIn)); if (isRestrictedAccess) { // only users in allow-users list can connect _standardAgentPermissions[NodePermissions::standardNameAnonymous]->canConnectToDomain = false; _standardAgentPermissions[NodePermissions::standardNameLoggedIn]->canConnectToDomain = false; } // else anonymous and logged-in retain default of canConnectToDomain = true foreach (QString allowedUser, allowedUsers) { // even if isRestrictedAccess is false, we have to add explicit rows for these users. // defaults to canConnectToDomain = true _agentPermissions[allowedUser].reset(new NodePermissions(allowedUser)); } foreach (QString allowedEditor, allowedEditors) { if (!_agentPermissions.contains(allowedEditor)) { _agentPermissions[allowedEditor].reset(new NodePermissions(allowedEditor)); if (isRestrictedAccess) { // they can change locks, but can't connect. _agentPermissions[allowedEditor]->canConnectToDomain = false; } } _agentPermissions[allowedEditor]->canAdjustLocks = true; } QList<QHash<QString, NodePermissionsPointer>> permissionsSets; permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get(); foreach (auto permissionsSet, permissionsSets) { foreach (QString userName, permissionsSet.keys()) { if (onlyEditorsAreRezzers) { permissionsSet[userName]->canRezPermanentEntities = permissionsSet[userName]->canAdjustLocks; permissionsSet[userName]->canRezTemporaryEntities = permissionsSet[userName]->canAdjustLocks; } else { permissionsSet[userName]->canRezPermanentEntities = true; permissionsSet[userName]->canRezTemporaryEntities = true; } } } packPermissions(); _standardAgentPermissions.clear(); _agentPermissions.clear(); } if (oldVersion < 1.5) { // This was prior to operating hours, so add default hours static const QString WEEKDAY_HOURS{ "descriptors.weekday_hours" }; static const QString WEEKEND_HOURS{ "descriptors.weekend_hours" }; static const QString UTC_OFFSET{ "descriptors.utc_offset" }; QVariant* weekdayHours = valueForKeyPath(_configMap.getUserConfig(), WEEKDAY_HOURS, true); QVariant* weekendHours = valueForKeyPath(_configMap.getUserConfig(), WEEKEND_HOURS, true); QVariant* utcOffset = valueForKeyPath(_configMap.getUserConfig(), UTC_OFFSET, true); *weekdayHours = QVariantList { QVariantMap{ { "open", QVariant("00:00") }, { "close", QVariant("23:59") } } }; *weekendHours = QVariantList { QVariantMap{ { "open", QVariant("00:00") }, { "close", QVariant("23:59") } } }; *utcOffset = QVariant(QTimeZone::systemTimeZone().offsetFromUtc(QDateTime::currentDateTime()) / (float)3600); // write the new settings to file persistToFile(); // reload the master and user config so the merged config is correct _configMap.loadMasterAndUserConfig(_argumentList); } } unpackPermissions(); // write the current description version to our settings appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion); } void DomainServerSettingsManager::packPermissionsForMap(QString mapName, NodePermissionsMap& agentPermissions, QString keyPath) { QVariant* security = valueForKeyPath(_configMap.getUserConfig(), "security"); if (!security || !security->canConvert(QMetaType::QVariantMap)) { security = valueForKeyPath(_configMap.getUserConfig(), "security", true); (*security) = QVariantMap(); } // save settings for anonymous / logged-in / localhost QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath); if (!permissions || !permissions->canConvert(QMetaType::QVariantList)) { permissions = valueForKeyPath(_configMap.getUserConfig(), keyPath, true); (*permissions) = QVariantList(); } QVariantList* permissionsList = reinterpret_cast<QVariantList*>(permissions); (*permissionsList).clear(); foreach (QString userName, agentPermissions.keys()) { *permissionsList += agentPermissions[userName]->toVariant(); } } void DomainServerSettingsManager::packPermissions() { // transfer details from _agentPermissions to _configMap packPermissionsForMap("standard_permissions", _standardAgentPermissions, AGENT_STANDARD_PERMISSIONS_KEYPATH); // save settings for specific users packPermissionsForMap("permissions", _agentPermissions, AGENT_PERMISSIONS_KEYPATH); persistToFile(); _configMap.loadMasterAndUserConfig(_argumentList); } void DomainServerSettingsManager::unpackPermissions() { // transfer details from _configMap to _agentPermissions; _standardAgentPermissions.clear(); _agentPermissions.clear(); bool foundLocalhost = false; bool foundAnonymous = false; bool foundLoggedIn = false; bool needPack = false; QVariant* standardPermissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_STANDARD_PERMISSIONS_KEYPATH); if (!standardPermissions || !standardPermissions->canConvert(QMetaType::QVariantList)) { qDebug() << "failed to extract standard permissions from settings."; standardPermissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_STANDARD_PERMISSIONS_KEYPATH, true); (*standardPermissions) = QVariantList(); } QVariant* permissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_PERMISSIONS_KEYPATH); if (!permissions || !permissions->canConvert(QMetaType::QVariantList)) { qDebug() << "failed to extract permissions from settings."; permissions = valueForKeyPath(_configMap.getUserConfig(), AGENT_PERMISSIONS_KEYPATH, true); (*permissions) = QVariantList(); } QList<QVariant> standardPermissionsList = standardPermissions->toList(); foreach (QVariant permsHash, standardPermissionsList) { NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; QString id = perms->getID(); foundLocalhost |= (id == NodePermissions::standardNameLocalhost); foundAnonymous |= (id == NodePermissions::standardNameAnonymous); foundLoggedIn |= (id == NodePermissions::standardNameLoggedIn); if (_standardAgentPermissions.contains(id)) { qDebug() << "duplicate name in standard permissions table: " << id; _standardAgentPermissions[id] |= perms; needPack = true; } else { _standardAgentPermissions[id] = perms; } } QList<QVariant> permissionsList = permissions->toList(); foreach (QVariant permsHash, permissionsList) { NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; QString id = perms->getID(); if (_agentPermissions.contains(id)) { qDebug() << "duplicate name in permissions table: " << id; _agentPermissions[id] |= perms; needPack = true; } else { _agentPermissions[id] = perms; } } // if any of the standard names are missing, add them if (!foundLocalhost) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLocalhost) }; perms->setAll(true); _standardAgentPermissions[perms->getID()] = perms; needPack = true; } if (!foundAnonymous) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameAnonymous) }; _standardAgentPermissions[perms->getID()] = perms; needPack = true; } if (!foundLoggedIn) { NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLoggedIn) }; _standardAgentPermissions[perms->getID()] = perms; needPack = true; } if (needPack) { packPermissions(); } #ifdef WANT_DEBUG qDebug() << "--------------- permissions ---------------------"; QList<QHash<QString, NodePermissionsPointer>> permissionsSets; permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get(); foreach (auto permissionSet, permissionsSets) { QHashIterator<QString, NodePermissionsPointer> i(permissionSet); while (i.hasNext()) { i.next(); NodePermissionsPointer perms = i.value(); qDebug() << i.key() << perms; } } #endif } NodePermissions DomainServerSettingsManager::getStandardPermissionsForName(const QString& name) const { if (_standardAgentPermissions.contains(name)) { return *(_standardAgentPermissions[name].get()); } NodePermissions nullPermissions; nullPermissions.setAll(false); return nullPermissions; } NodePermissions DomainServerSettingsManager::getPermissionsForName(const QString& name) const { if (_agentPermissions.contains(name)) { return *(_agentPermissions[name].get()); } NodePermissions nullPermissions; nullPermissions.setAll(false); return nullPermissions; } QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath); if (foundValue) { return *foundValue; } else { int dotIndex = keyPath.indexOf('.'); QString groupKey = keyPath.mid(0, dotIndex); QString settingKey = keyPath.mid(dotIndex + 1); foreach(const QVariant& group, _descriptionArray.toVariantList()) { QVariantMap groupMap = group.toMap(); if (groupMap[DESCRIPTION_NAME_KEY].toString() == groupKey) { foreach(const QVariant& setting, groupMap[DESCRIPTION_SETTINGS_KEY].toList()) { QVariantMap settingMap = setting.toMap(); if (settingMap[DESCRIPTION_NAME_KEY].toString() == settingKey) { return settingMap[SETTING_DEFAULT_KEY]; } } return QVariant(); } } } return QVariant(); } bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) { if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH_JSON) { // this is a GET operation for our settings // check if there is a query parameter for settings affecting a particular type of assignment const QString SETTINGS_TYPE_QUERY_KEY = "type"; QUrlQuery settingsQuery(url); QString typeValue = settingsQuery.queryItemValue(SETTINGS_TYPE_QUERY_KEY); if (!typeValue.isEmpty()) { QJsonObject responseObject = responseObjectForType(typeValue); connection->respond(HTTPConnection::StatusCode200, QJsonDocument(responseObject).toJson(), "application/json"); return true; } else { return false; } } return false; } bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection *connection, const QUrl &url) { if (connection->requestOperation() == QNetworkAccessManager::PostOperation && url.path() == SETTINGS_PATH_JSON) { // this is a POST operation to change one or more settings QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent()); QJsonObject postedObject = postedDocument.object(); qDebug() << "DomainServerSettingsManager postedObject -" << postedObject; // we recurse one level deep below each group for the appropriate setting bool restartRequired = recurseJSONObjectAndOverwriteSettings(postedObject); // store whatever the current _settingsMap is to file persistToFile();