852 lines · 37.4 KB · C++
RawDownload
////  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.