use DomainServerSettingsManager to handle simulation settings and config

This commit is contained in:
Stephen Birarda 2014-09-26 11:06:05 -07:00
parent 2c8d8decb8
commit 954fbc2017
8 changed files with 142 additions and 60 deletions

View file

@ -119,7 +119,7 @@ function reloadSettings() {
var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!";
$('#settings').on('click', 'button', function(e){
$('body').on('click', '.save-button', function(e){
// disable any inputs not changed
$("input:not([data-changed])").each(function(){
$(this).prop('disabled', true);
@ -133,6 +133,9 @@ $('#settings').on('click', 'button', function(e){
$(this).prop('disabled', false);
});
// remove focus from the button
$(this).blur()
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
$.ajax('/settings.json', {
data: JSON.stringify(formJSON),

View file

@ -22,7 +22,7 @@
</ul>
<button id="advanced-toggle-button" class="btn btn-info">Show advanced</button>
<button id="save-button" class="btn btn-success">Save and restart</button>
<button class="btn btn-success save-button">Save and restart</button>
</div>
</div>

View file

@ -48,13 +48,14 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_cookieSessionHash(),
_settingsManager()
{
LogUtils::init();
setOrganizationName("High Fidelity");
setOrganizationDomain("highfidelity.io");
setApplicationName("domain-server");
QSettings::setDefaultFormat(QSettings::IniFormat);
_settingsManager.loadSettingsMap(arguments());
installNativeEventFilter(&_shutdownEventListener);
connect(&_shutdownEventListener, SIGNAL(receivedCloseEvent()), SLOT(quit()));
@ -62,8 +63,6 @@ DomainServer::DomainServer(int argc, char* argv[]) :
qRegisterMetaType<DomainServerWebSessionData>("DomainServerWebSessionData");
qRegisterMetaTypeStreamOperators<DomainServerWebSessionData>("DomainServerWebSessionData");
_argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments());
if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) {
// we either read a certificate and private key or were not passed one
// and completed login or did not need to
@ -83,8 +82,8 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() {
const QString X509_PRIVATE_KEY_OPTION = "key";
const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE";
QString certPath = _argumentVariantMap.value(X509_CERTIFICATE_OPTION).toString();
QString keyPath = _argumentVariantMap.value(X509_PRIVATE_KEY_OPTION).toString();
QString certPath = _settingsManager.getSettingsMap().value(X509_CERTIFICATE_OPTION).toString();
QString keyPath = _settingsManager.getSettingsMap().value(X509_PRIVATE_KEY_OPTION).toString();
if (!certPath.isEmpty() && !keyPath.isEmpty()) {
// the user wants to use DTLS to encrypt communication with nodes
@ -143,10 +142,11 @@ bool DomainServer::optionallySetupOAuth() {
const QString OAUTH_CLIENT_SECRET_ENV = "DOMAIN_SERVER_CLIENT_SECRET";
const QString REDIRECT_HOSTNAME_OPTION = "hostname";
_oauthProviderURL = QUrl(_argumentVariantMap.value(OAUTH_PROVIDER_URL_OPTION).toString());
_oauthClientID = _argumentVariantMap.value(OAUTH_CLIENT_ID_OPTION).toString();
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
_oauthProviderURL = QUrl(settingsMap.value(OAUTH_PROVIDER_URL_OPTION).toString());
_oauthClientID = settingsMap.value(OAUTH_CLIENT_ID_OPTION).toString();
_oauthClientSecret = QProcessEnvironment::systemEnvironment().value(OAUTH_CLIENT_SECRET_ENV);
_hostname = _argumentVariantMap.value(REDIRECT_HOSTNAME_OPTION).toString();
_hostname = settingsMap.value(REDIRECT_HOSTNAME_OPTION).toString();
if (!_oauthClientID.isEmpty()) {
if (_oauthProviderURL.isEmpty()
@ -171,9 +171,11 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
const QString CUSTOM_PORT_OPTION = "port";
unsigned short domainServerPort = DEFAULT_DOMAIN_SERVER_PORT;
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
if (_argumentVariantMap.contains(CUSTOM_PORT_OPTION)) {
domainServerPort = (unsigned short) _argumentVariantMap.value(CUSTOM_PORT_OPTION).toUInt();
if (settingsMap.contains(CUSTOM_PORT_OPTION)) {
domainServerPort = (unsigned short) settingsMap.value(CUSTOM_PORT_OPTION).toUInt();
}
unsigned short domainServerDTLSPort = 0;
@ -183,8 +185,8 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
const QString CUSTOM_DTLS_PORT_OPTION = "dtls-port";
if (_argumentVariantMap.contains(CUSTOM_DTLS_PORT_OPTION)) {
domainServerDTLSPort = (unsigned short) _argumentVariantMap.value(CUSTOM_DTLS_PORT_OPTION).toUInt();
if (settingsMap.contains(CUSTOM_DTLS_PORT_OPTION)) {
domainServerDTLSPort = (unsigned short) settingsMap.value(CUSTOM_DTLS_PORT_OPTION).toUInt();
}
}
@ -197,7 +199,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
// set our LimitedNodeList UUID to match the UUID from our config
// nodes will currently use this to add resources to data-web that relate to our domain
nodeList->setSessionUUID(_argumentVariantMap.value(DOMAIN_CONFIG_ID_KEY).toString());
nodeList->setSessionUUID(settingsMap.value(DOMAIN_CONFIG_ID_KEY).toString());
connect(nodeList, &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);
connect(nodeList, &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled);
@ -260,9 +262,10 @@ bool DomainServer::hasOAuthProviderAndAuthInformation() {
bool DomainServer::optionallySetupAssignmentPayment() {
const QString PAY_FOR_ASSIGNMENTS_OPTION = "pay-for-assignments";
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
if (_argumentVariantMap.contains(PAY_FOR_ASSIGNMENTS_OPTION) &&
_argumentVariantMap.value(PAY_FOR_ASSIGNMENTS_OPTION).toBool() &&
if (settingsMap.contains(PAY_FOR_ASSIGNMENTS_OPTION) &&
settingsMap.value(PAY_FOR_ASSIGNMENTS_OPTION).toBool() &&
hasOAuthProviderAndAuthInformation()) {
qDebug() << "Assignments will be paid for via" << qPrintable(_oauthProviderURL.toString());
@ -288,8 +291,10 @@ bool DomainServer::optionallySetupAssignmentPayment() {
void DomainServer::setupDynamicIPAddressUpdating() {
const QString ENABLE_DYNAMIC_IP_UPDATING_OPTION = "update-ip";
if (_argumentVariantMap.contains(ENABLE_DYNAMIC_IP_UPDATING_OPTION) &&
_argumentVariantMap.value(ENABLE_DYNAMIC_IP_UPDATING_OPTION).toBool() &&
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
if (settingsMap.contains(ENABLE_DYNAMIC_IP_UPDATING_OPTION) &&
settingsMap.value(ENABLE_DYNAMIC_IP_UPDATING_OPTION).toBool() &&
hasOAuthProviderAndAuthInformation()) {
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
@ -338,9 +343,11 @@ void DomainServer::parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes)
// check for configs from the command line, these take precedence
const QString ASSIGNMENT_CONFIG_REGEX_STRING = "config-([\\d]+)";
QRegExp assignmentConfigRegex(ASSIGNMENT_CONFIG_REGEX_STRING);
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
// scan for assignment config keys
QStringList variantMapKeys = _argumentVariantMap.keys();
QStringList variantMapKeys = settingsMap.keys();
int configIndex = variantMapKeys.indexOf(assignmentConfigRegex);
while (configIndex != -1) {
@ -348,7 +355,7 @@ void DomainServer::parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes)
Assignment::Type assignmentType = (Assignment::Type) assignmentConfigRegex.cap(1).toInt();
if (assignmentType < Assignment::AllTypes && !excludedTypes.contains(assignmentType)) {
QVariant mapValue = _argumentVariantMap[variantMapKeys[configIndex]];
QVariant mapValue = settingsMap[variantMapKeys[configIndex]];
QJsonArray assignmentArray;
if (mapValue.type() == QVariant::String) {
@ -513,7 +520,7 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
QString connectedUsername;
if (!isAssignment && !_oauthProviderURL.isEmpty() && _argumentVariantMap.contains(ALLOWED_ROLES_CONFIG_KEY)) {
if (!isAssignment && !_oauthProviderURL.isEmpty() && _settingsManager.getSettingsMap().contains(ALLOWED_ROLES_CONFIG_KEY)) {
// this is an Agent, and we require authentication so we can compare the user's roles to our list of allowed ones
if (_sessionAuthenticationHash.contains(packetUUID)) {
connectedUsername = _sessionAuthenticationHash.take(packetUUID);
@ -1392,8 +1399,10 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server.";
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
if (!_oauthProviderURL.isEmpty()
&& (_argumentVariantMap.contains(ADMIN_USERS_CONFIG_KEY) || _argumentVariantMap.contains(ADMIN_ROLES_CONFIG_KEY))) {
&& (settingsMap.contains(ADMIN_USERS_CONFIG_KEY) || settingsMap.contains(ADMIN_ROLES_CONFIG_KEY))) {
QString cookieString = connection->requestHeaders().value(HTTP_COOKIE_HEADER_KEY);
const QString COOKIE_UUID_REGEX_STRING = HIFI_SESSION_COOKIE_KEY + "=([\\d\\w-]+)($|;)";
@ -1404,7 +1413,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
cookieUUID = cookieUUIDRegex.cap(1);
}
if (_argumentVariantMap.contains(BASIC_AUTH_CONFIG_KEY)) {
if (settingsMap.contains(BASIC_AUTH_CONFIG_KEY)) {
qDebug() << "Config file contains web admin settings for OAuth and basic HTTP authentication."
<< "These cannot be combined - using OAuth for authentication.";
}
@ -1414,13 +1423,13 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
DomainServerWebSessionData sessionData = _cookieSessionHash.value(cookieUUID);
QString profileUsername = sessionData.getUsername();
if (_argumentVariantMap.value(ADMIN_USERS_CONFIG_KEY).toJsonValue().toArray().contains(profileUsername)) {
if (settingsMap.value(ADMIN_USERS_CONFIG_KEY).toJsonValue().toArray().contains(profileUsername)) {
// this is an authenticated user
return true;
}
// loop the roles of this user and see if they are in the admin-roles array
QJsonArray adminRolesArray = _argumentVariantMap.value(ADMIN_ROLES_CONFIG_KEY).toJsonValue().toArray();
QJsonArray adminRolesArray = settingsMap.value(ADMIN_ROLES_CONFIG_KEY).toJsonValue().toArray();
if (!adminRolesArray.isEmpty()) {
foreach(const QString& userRole, sessionData.getRoles()) {
@ -1455,7 +1464,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
// we don't know about this user yet, so they are not yet authenticated
return false;
}
} else if (_argumentVariantMap.contains(BASIC_AUTH_CONFIG_KEY)) {
} else if (settingsMap.contains(BASIC_AUTH_CONFIG_KEY)) {
// config file contains username and password combinations for basic auth
const QByteArray BASIC_AUTH_HEADER_KEY = "Authorization";
@ -1474,7 +1483,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
QString password = credentialList[1];
// we've pulled a username and password - now check if there is a match in our basic auth hash
QJsonObject basicAuthObject = _argumentVariantMap.value(BASIC_AUTH_CONFIG_KEY).toJsonValue().toObject();
QJsonObject basicAuthObject = settingsMap.value(BASIC_AUTH_CONFIG_KEY).toJsonValue().toObject();
if (basicAuthObject.contains(username)) {
const QString BASIC_AUTH_USER_PASSWORD_KEY = "password";
@ -1557,7 +1566,8 @@ void DomainServer::handleProfileRequestFinished() {
// pull the user roles from the response
QJsonArray userRolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray();
QJsonArray allowedRolesArray = _argumentVariantMap.value(ALLOWED_ROLES_CONFIG_KEY).toJsonValue().toArray();
QJsonArray allowedRolesArray = _settingsManager.getSettingsMap()
.value(ALLOWED_ROLES_CONFIG_KEY).toJsonValue().toArray();
QString connectableUsername;
QString profileUsername = profileJSON.object()["data"].toObject()["user"].toObject()["username"].toString();

View file

@ -115,8 +115,6 @@ private:
QHash<QUuid, PendingAssignedNodeData*> _pendingAssignedNodes;
TransactionHash _pendingAssignmentCredits;
QVariantMap _argumentVariantMap;
bool _isUsingDTLS;
QUrl _oauthProviderURL;

View file

@ -10,19 +10,21 @@
//
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonObject>
#include <QtCore/QStandardPaths>
#include <QtCore/QUrl>
#include <QtCore/QUrlQuery>
#include <Assignment.h>
#include <HifiConfigVariantMap.h>
#include <HTTPConnection.h>
#include "DomainServerSettingsManager.h"
const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json";
const QString SETTINGS_JSON_FILE_RELATIVE_PATH = "/resources/settings.json";
DomainServerSettingsManager::DomainServerSettingsManager() :
_descriptionArray(),
@ -33,20 +35,20 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
descriptionFile.open(QIODevice::ReadOnly);
_descriptionArray = QJsonDocument::fromJson(descriptionFile.readAll()).array();
}
void DomainServerSettingsManager::loadSettingsMap(const QStringList& argumentList) {
_settingsMap = HifiConfigVariantMap::mergeMasterConfigWithUserConfig(argumentList);
// load the existing config file to get the current values
QFile configFile(QCoreApplication::applicationDirPath() + SETTINGS_JSON_FILE_RELATIVE_PATH);
qDebug() << _settingsMap;
if (configFile.exists()) {
configFile.open(QIODevice::ReadOnly);
_settingsMap = QJsonDocument::fromJson(configFile.readAll()).toVariant().toMap();
}
// figure out where we are supposed to persist our settings to
_settingsFilepath = HifiConfigVariantMap::userConfigFilepath(argumentList);
}
const QString DESCRIPTION_SETTINGS_KEY = "settings";
const QString SETTING_DEFAULT_KEY = "default";
const QString SETTINGS_GROUP_KEY_NAME = "key";
const QString SETTINGS_GROUP_KEY_NAME = "name";
bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) {
if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == "/settings.json") {
@ -209,7 +211,15 @@ QByteArray DomainServerSettingsManager::getJSONSettingsMap() const {
}
void DomainServerSettingsManager::persistToFile() {
QFile settingsFile(QCoreApplication::applicationDirPath() + SETTINGS_JSON_FILE_RELATIVE_PATH);
// make sure we have the dir the settings file is supposed to live in
QFileInfo settingsFileInfo(_settingsFilepath);
if (!settingsFileInfo.dir().exists()) {
settingsFileInfo.dir().mkpath(".");
}
QFile settingsFile(_settingsFilepath);
if (settingsFile.open(QIODevice::WriteOnly)) {
settingsFile.write(getJSONSettingsMap());

View file

@ -24,7 +24,10 @@ public:
bool handlePublicHTTPRequest(HTTPConnection* connection, const QUrl& url);
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
void loadSettingsMap(const QStringList& argumentList);
QByteArray getJSONSettingsMap() const;
const QVariantMap& getSettingsMap() const { return _settingsMap; }
private:
void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant,
QJsonArray descriptionArray);
@ -32,6 +35,7 @@ private:
QJsonArray _descriptionArray;
QVariantMap _settingsMap;
QString _settingsFilepath;
};
#endif // hifi_DomainServerSettingsManager_h

View file

@ -82,26 +82,78 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL
QCoreApplication::applicationName());
}
QFile configFile(configFilePath);
if (configFile.exists()) {
qDebug() << "Reading JSON config file at" << configFilePath;
configFile.open(QIODevice::ReadOnly);
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
QJsonObject rootObject = configDocument.object();
// enumerate the keys of the configDocument object
foreach(const QString& key, rootObject.keys()) {
if (!mergedMap.contains(key)) {
// no match in existing list, add it
mergedMap.insert(key, QVariant(rootObject[key]));
}
}
} else {
qDebug() << "Could not find JSON config file at" << configFilePath;
}
return mergedMap;
}
QVariantMap HifiConfigVariantMap::mergeMasterConfigWithUserConfig(const QStringList& argumentList) {
// check if there is a master config file
const QString MASTER_CONFIG_FILE_OPTION = "--master-config";
QVariantMap configVariantMap;
int masterConfigIndex = argumentList.indexOf(MASTER_CONFIG_FILE_OPTION);
if (masterConfigIndex != -1) {
QString masterConfigFilepath = argumentList[masterConfigIndex + 1];
mergeMapWithJSONFile(configVariantMap, masterConfigFilepath);
}
// merge the existing configVariantMap with the user config file
mergeMapWithJSONFile(configVariantMap, userConfigFilepath(argumentList));
return configVariantMap;
}
QString HifiConfigVariantMap::userConfigFilepath(const QStringList& argumentList) {
// we've loaded up the master config file, now fill in anything it didn't have with the user config file
const QString USER_CONFIG_FILE_OPTION = "--user-config";
int userConfigIndex = argumentList.indexOf(USER_CONFIG_FILE_OPTION);
QString userConfigFilepath;
if (userConfigIndex != -1) {
userConfigFilepath = argumentList[userConfigIndex + 1];
} else {
userConfigFilepath = QString("%1/%2/%3/config.json").arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation),
QCoreApplication::organizationName(),
QCoreApplication::applicationName());
}
return userConfigFilepath;
}
void HifiConfigVariantMap::mergeMapWithJSONFile(QVariantMap& existingMap, const QString& filename) {
QFile configFile(filename);
if (configFile.exists()) {
qDebug() << "Reading JSON config file at" << filename;
configFile.open(QIODevice::ReadOnly);
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
if (existingMap.isEmpty()) {
existingMap = configDocument.toVariant().toMap();
} else {
addMissingValuesToExistingMap(existingMap, configDocument.toVariant().toMap());
}
} else {
qDebug() << "Could not find JSON config file at" << filename;
}
}
void HifiConfigVariantMap::addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap) {
foreach(const QString& key, newMap.keys()) {
if (existingMap.contains(key)) {
// if this is just a regular value, we're done - we don't ovveride
if (newMap[key].canConvert(QMetaType::QVariantMap) && existingMap[key].canConvert(QMetaType::QVariantMap)) {
// there's a variant map below and the existing map has one too, so we need to keep recursing
addMissingValuesToExistingMap(reinterpret_cast<QVariantMap&>(existingMap[key]), newMap[key].toMap());
}
} else {
existingMap[key] = newMap[key];
}
}
}

View file

@ -17,6 +17,11 @@
class HifiConfigVariantMap {
public:
static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList);
static QVariantMap mergeMasterConfigWithUserConfig(const QStringList& argumentList);
static QString userConfigFilepath(const QStringList& argumentList);
private:
static void mergeMapWithJSONFile(QVariantMap& existingMap, const QString& filename);
static void addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap);
};
#endif // hifi_HifiConfigVariantMap_h