mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
- The first part
- Added CodeDocs config file for customization - Fixing LGTM alerts - LGTM bug fixed again - added token option to hyperion-remote - fix DBManager::getDB() - next bugfix - correct broken signal from SettingManager to Hyperion - Token list is created after the schema is fetched Signed-off-by: Paulchen-Panther <Paulchen-Panter@protonmail.com>
This commit is contained in:
164
libsrc/hyperion/AuthManager.cpp
Normal file
164
libsrc/hyperion/AuthManager.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
#include <hyperion/AuthManager.h>
|
||||
|
||||
// util
|
||||
#include <db/AuthTable.h>
|
||||
|
||||
// qt
|
||||
#include <QJsonObject>
|
||||
#include <QTimer>
|
||||
|
||||
AuthManager* AuthManager::manager = nullptr;
|
||||
|
||||
AuthManager::AuthManager(const QString& rootPath, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _authTable(new AuthTable(rootPath, this))
|
||||
, _pendingRequests()
|
||||
, _authRequired(true)
|
||||
, _timer(new QTimer(this))
|
||||
{
|
||||
AuthManager::manager = this;
|
||||
|
||||
// setup timer
|
||||
_timer->setInterval(1000);
|
||||
connect(_timer, &QTimer::timeout, this, &AuthManager::checkTimeout);
|
||||
|
||||
// init with default user and password
|
||||
if(!_authTable->userExist("Hyperion"))
|
||||
{
|
||||
_authTable->createUser("Hyperion","hyperion");
|
||||
}
|
||||
}
|
||||
|
||||
const bool & AuthManager::isAuthRequired()
|
||||
{
|
||||
return _authRequired;
|
||||
}
|
||||
|
||||
const bool & AuthManager::isLocalAuthRequired()
|
||||
{
|
||||
return _localAuthRequired;
|
||||
}
|
||||
|
||||
const AuthManager::AuthDefinition AuthManager::createToken(const QString& comment)
|
||||
{
|
||||
const QString token = QUuid::createUuid().toString().mid(1, 36);
|
||||
const QString id = QUuid::createUuid().toString().mid(1, 36).left(5);
|
||||
|
||||
_authTable->createToken(token, comment, id);
|
||||
|
||||
AuthDefinition def;
|
||||
def.comment = comment;
|
||||
def.token = token;
|
||||
def.id = id;
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
const QVector<AuthManager::AuthDefinition> AuthManager::getTokenList()
|
||||
{
|
||||
QVector<QVariantMap> vector = _authTable->getTokenList();
|
||||
QVector<AuthManager::AuthDefinition> finalVec;
|
||||
for(const auto& entry : vector)
|
||||
{
|
||||
AuthDefinition def;
|
||||
def.comment = entry["comment"].toString();
|
||||
def.id = entry["id"].toString();
|
||||
def.lastUse = entry["last_use"].toString();
|
||||
|
||||
// don't add empty ids
|
||||
if(!entry["id"].toString().isEmpty())
|
||||
finalVec.append(def);
|
||||
}
|
||||
return finalVec;
|
||||
}
|
||||
|
||||
const bool AuthManager::isUserAuthorized(const QString& user, const QString& pw)
|
||||
{
|
||||
return _authTable->isUserAuthorized(user, pw);
|
||||
}
|
||||
|
||||
const bool AuthManager::isTokenAuthorized(const QString& token)
|
||||
{
|
||||
return _authTable->tokenExist(token);
|
||||
}
|
||||
|
||||
void AuthManager::setNewTokenRequest(QObject* caller, const QString& comment, const QString& id)
|
||||
{
|
||||
if(!_pendingRequests.contains(id))
|
||||
{
|
||||
AuthDefinition newDef {id, comment, caller, uint64_t(QDateTime::currentMSecsSinceEpoch()+60000)};
|
||||
_pendingRequests[id] = newDef;
|
||||
_timer->start();
|
||||
emit newPendingTokenRequest(id, comment);
|
||||
}
|
||||
}
|
||||
|
||||
const bool AuthManager::acceptTokenRequest(const QString& id)
|
||||
{
|
||||
if(_pendingRequests.contains(id))
|
||||
{
|
||||
const QString token = QUuid::createUuid().toString().remove("{").remove("}");
|
||||
AuthDefinition def = _pendingRequests.take(id);
|
||||
_authTable->createToken(token, def.comment, id);
|
||||
emit tokenResponse(true, def.caller, token, def.comment, id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool AuthManager::denyTokenRequest(const QString& id)
|
||||
{
|
||||
if(_pendingRequests.contains(id))
|
||||
{
|
||||
AuthDefinition def = _pendingRequests.take(id);
|
||||
emit tokenResponse(false, def.caller, QString(), def.comment, id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const QMap<QString, AuthManager::AuthDefinition> AuthManager::getPendingRequests()
|
||||
{
|
||||
return _pendingRequests;
|
||||
}
|
||||
|
||||
const bool AuthManager::deleteToken(const QString& id)
|
||||
{
|
||||
if(_authTable->deleteToken(id))
|
||||
{
|
||||
//emit tokenDeleted(token);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AuthManager::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::NETWORK)
|
||||
{
|
||||
const QJsonObject& obj = config.object();
|
||||
_authRequired = obj["apiAuth"].toBool(true);
|
||||
_localAuthRequired = obj["localApiAuth"].toBool(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AuthManager::checkTimeout()
|
||||
{
|
||||
const uint64_t now = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
QMapIterator<QString, AuthDefinition> i(_pendingRequests);
|
||||
while (i.hasNext())
|
||||
{
|
||||
i.next();
|
||||
|
||||
const AuthDefinition& def = i.value();
|
||||
if(def.timeoutTime <= now)
|
||||
{
|
||||
emit tokenResponse(false, def.caller, QString(), def.comment, def.id);
|
||||
_pendingRequests.remove(i.key());
|
||||
}
|
||||
}
|
||||
// abort if empty
|
||||
if(_pendingRequests.isEmpty())
|
||||
_timer->stop();
|
||||
}
|
@@ -25,5 +25,6 @@ target_link_libraries(hyperion
|
||||
bonjour
|
||||
boblightserver
|
||||
effectengine
|
||||
database
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
@@ -71,7 +71,7 @@ Hyperion* Hyperion::getInstance()
|
||||
|
||||
Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString configFile, const QString rootPath)
|
||||
: _daemon(daemon)
|
||||
, _settingsManager(new SettingsManager(this, instance, configFile))
|
||||
, _settingsManager(new SettingsManager(instance, configFile, this))
|
||||
, _componentRegister(this)
|
||||
, _ledString(hyperion::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object())))
|
||||
, _ledStringClone(hyperion::createLedStringClone(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object())))
|
||||
@@ -89,6 +89,9 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
, _prevCompId(hyperion::COMP_INVALID)
|
||||
, _ledBuffer(_ledString.leds().size(), ColorRgb::BLACK)
|
||||
{
|
||||
// forward settings changed to Hyperion
|
||||
connect(_settingsManager, &SettingsManager::settingsChanged, this, &Hyperion::settingsChanged);
|
||||
|
||||
if (!_raw2ledAdjustment->verifyAdjustments())
|
||||
Warning(_log, "At least one led has no color calibration, please add all leds from your led layout to an 'LED index' field!");
|
||||
|
||||
@@ -209,9 +212,6 @@ void Hyperion::handleSettingsUpdate(const settings::type& type, const QJsonDocum
|
||||
|
||||
const QJsonArray leds = config.array();
|
||||
|
||||
// // lock update()
|
||||
// _lockUpdate = true;
|
||||
|
||||
// stop and cache all running effects, as effects depend heavily on ledlayout
|
||||
_effectEngine->cacheRunningEffects();
|
||||
|
||||
@@ -247,14 +247,10 @@ void Hyperion::handleSettingsUpdate(const settings::type& type, const QJsonDocum
|
||||
|
||||
// start cached effects
|
||||
_effectEngine->startCachedEffects();
|
||||
|
||||
// // unlock
|
||||
// _lockUpdate = false;
|
||||
}
|
||||
else if(type == settings::DEVICE)
|
||||
{
|
||||
QMutexLocker lock(&_changes);
|
||||
// _lockUpdate = true;
|
||||
QJsonObject dev = config.object();
|
||||
|
||||
// handle hwLedCount update
|
||||
@@ -278,7 +274,6 @@ void Hyperion::handleSettingsUpdate(const settings::type& type, const QJsonDocum
|
||||
// do always reinit until the led devices can handle dynamic changes
|
||||
dev["currentLedCount"] = int(_hwLedCount); // Inject led count info
|
||||
_ledDeviceWrapper->createLedDevice(dev);
|
||||
// _lockUpdate = false;
|
||||
}
|
||||
// update once to push single color sets / adjustments/ ledlayout resizes and update ledBuffer color
|
||||
update();
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
// util
|
||||
#include <utils/JsonUtils.h>
|
||||
#include <db/SettingsTable.h>
|
||||
|
||||
// json schema process
|
||||
#include <utils/jsonschema/QJsonFactory.h>
|
||||
@@ -16,11 +17,11 @@
|
||||
|
||||
QJsonObject SettingsManager::schemaJson;
|
||||
|
||||
SettingsManager::SettingsManager(Hyperion* hyperion, const quint8& instance, const QString& configFile)
|
||||
SettingsManager::SettingsManager(const quint8& instance, const QString& configFile, Hyperion* hyperion)
|
||||
: _hyperion(hyperion)
|
||||
, _log(Logger::getInstance("SettingsManager"))
|
||||
, _sTable(new SettingsTable(instance, this))
|
||||
{
|
||||
connect(this, &SettingsManager::settingsChanged, _hyperion, &Hyperion::settingsChanged);
|
||||
// get schema
|
||||
if(schemaJson.isEmpty())
|
||||
{
|
||||
@@ -34,11 +35,14 @@ SettingsManager::SettingsManager(Hyperion* hyperion, const quint8& instance, con
|
||||
throw std::runtime_error(error.what());
|
||||
}
|
||||
}
|
||||
|
||||
// get default config
|
||||
QJsonObject defaultConfig;
|
||||
if(!JsonUtils::readFile(":/hyperion_default.config", defaultConfig, _log))
|
||||
throw std::runtime_error("Failed to read default config");
|
||||
|
||||
// TODO BEGIN - remove when database migration is done
|
||||
|
||||
Info(_log, "Selected configuration file: %s", QSTRING_CSTR(configFile));
|
||||
QJsonSchemaChecker schemaCheckerT;
|
||||
schemaCheckerT.setSchema(schemaJson);
|
||||
@@ -70,76 +74,74 @@ SettingsManager::SettingsManager(Hyperion* hyperion, const quint8& instance, con
|
||||
throw std::runtime_error("ERROR: Can't save configuration file, aborting");
|
||||
}
|
||||
|
||||
Debug(_log,"Settings database initialized")
|
||||
}
|
||||
// TODO END - remove when database migration is done
|
||||
|
||||
SettingsManager::SettingsManager(const quint8& instance, const QString& configFile)
|
||||
: _hyperion(nullptr)
|
||||
, _log(Logger::getInstance("SettingsManager"))
|
||||
{
|
||||
Q_INIT_RESOURCE(resource);
|
||||
// get schema
|
||||
if(schemaJson.isEmpty())
|
||||
// transform json to string lists
|
||||
QStringList keyList = defaultConfig.keys();
|
||||
QStringList defValueList;
|
||||
for(const auto key : keyList)
|
||||
{
|
||||
try
|
||||
if(defaultConfig[key].isObject())
|
||||
{
|
||||
schemaJson = QJsonFactory::readSchema(":/hyperion-schema");
|
||||
defValueList << QString(QJsonDocument(defaultConfig[key].toObject()).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
catch(const std::runtime_error& error)
|
||||
else if(defaultConfig[key].isArray())
|
||||
{
|
||||
throw std::runtime_error(error.what());
|
||||
defValueList << QString(QJsonDocument(defaultConfig[key].toArray()).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
}
|
||||
// get default config
|
||||
QJsonObject defaultConfig;
|
||||
if(!JsonUtils::readFile(":/hyperion_default.config", defaultConfig, _log))
|
||||
throw std::runtime_error("Failed to read default config");
|
||||
|
||||
Info(_log, "Selected configuration file: %s", QSTRING_CSTR(configFile));
|
||||
QJsonSchemaChecker schemaCheckerT;
|
||||
schemaCheckerT.setSchema(schemaJson);
|
||||
|
||||
if(!JsonUtils::readFile(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("Failed to load config!");
|
||||
|
||||
// validate config with schema and correct it if required
|
||||
QPair<bool, bool> validate = schemaCheckerT.validate(_qconfig);
|
||||
|
||||
// errors in schema syntax, abort
|
||||
if (!validate.second)
|
||||
// fill database with default data if required
|
||||
for(const auto key : keyList)
|
||||
{
|
||||
foreach (auto & schemaError, schemaCheckerT.getMessages())
|
||||
QString val = defValueList.takeFirst();
|
||||
// prevent overwrite
|
||||
if(!_sTable->recordExist(key))
|
||||
_sTable->createSettingsRecord(key,val);
|
||||
}
|
||||
|
||||
// need to validate all data in database constuct the entire data object
|
||||
// TODO refactor schemaChecker to accept QJsonArray in validate(); QJsonDocument container? To validate them per entry...
|
||||
QJsonObject dbConfig;
|
||||
for(const auto key : keyList)
|
||||
{
|
||||
QJsonDocument doc = _sTable->getSettingsRecord(key);
|
||||
if(doc.isArray())
|
||||
dbConfig[key] = doc.array();
|
||||
else
|
||||
dbConfig[key] = doc.object();
|
||||
}
|
||||
|
||||
// validate full dbconfig against schema, on error we need to rewrite entire table
|
||||
QJsonSchemaChecker schemaChecker;
|
||||
schemaChecker.setSchema(schemaJson);
|
||||
QPair<bool,bool> valid = schemaChecker.validate(dbConfig);
|
||||
// check if our main schema syntax is IO
|
||||
if (!valid.second)
|
||||
{
|
||||
foreach (auto & schemaError, schemaChecker.getMessages())
|
||||
Error(_log, "Schema Syntax Error: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
throw std::runtime_error("ERROR: Hyperion schema has syntax errors!");
|
||||
throw std::runtime_error("The config schema has invalid syntax. This should never happen! Go fix it!");
|
||||
}
|
||||
// errors in configuration, correct it!
|
||||
if (!validate.first)
|
||||
if (!valid.first)
|
||||
{
|
||||
Warning(_log,"Errors have been found in the configuration file. Automatic correction has been applied");
|
||||
_qconfig = schemaCheckerT.getAutoCorrectedConfig(_qconfig);
|
||||
Info(_log,"Table upgrade required...");
|
||||
dbConfig = schemaChecker.getAutoCorrectedConfig(dbConfig);
|
||||
|
||||
foreach (auto & schemaError, schemaCheckerT.getMessages())
|
||||
foreach (auto & schemaError, schemaChecker.getMessages())
|
||||
Warning(_log, "Config Fix: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
if (!JsonUtils::write(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("ERROR: Can't save configuration file, aborting");
|
||||
saveSettings(dbConfig);
|
||||
}
|
||||
else
|
||||
_qconfig = dbConfig;
|
||||
|
||||
Debug(_log,"Settings database initialized")
|
||||
}
|
||||
|
||||
SettingsManager::~SettingsManager()
|
||||
{
|
||||
}
|
||||
|
||||
const QJsonDocument SettingsManager::getSetting(const settings::type& type)
|
||||
{
|
||||
QString key = settings::typeToString(type);
|
||||
if(_qconfig[key].isObject())
|
||||
return QJsonDocument(_qconfig[key].toObject());
|
||||
else
|
||||
return QJsonDocument(_qconfig[key].toArray());
|
||||
return _sTable->getSettingsRecord(settings::typeToString(type));
|
||||
}
|
||||
|
||||
bool SettingsManager::saveSettings(QJsonObject config, const bool& correct)
|
||||
@@ -168,25 +170,34 @@ bool SettingsManager::saveSettings(QJsonObject config, const bool& correct)
|
||||
return false;
|
||||
}
|
||||
|
||||
// compare old data with new data to emit/save changes accordingly
|
||||
for(const auto key : config.keys())
|
||||
{
|
||||
QString newData, oldData;
|
||||
|
||||
_qconfig[key].isObject()
|
||||
? oldData = QString(QJsonDocument(_qconfig[key].toObject()).toJson(QJsonDocument::Compact))
|
||||
: oldData = QString(QJsonDocument(_qconfig[key].toArray()).toJson(QJsonDocument::Compact));
|
||||
|
||||
config[key].isObject()
|
||||
? newData = QString(QJsonDocument(config[key].toObject()).toJson(QJsonDocument::Compact))
|
||||
: newData = QString(QJsonDocument(config[key].toArray()).toJson(QJsonDocument::Compact));
|
||||
|
||||
if(oldData != newData)
|
||||
emit settingsChanged(settings::stringToType(key), QJsonDocument::fromJson(newData.toLocal8Bit()));
|
||||
}
|
||||
|
||||
// store the current state
|
||||
// store the new config
|
||||
_qconfig = config;
|
||||
|
||||
// extract keys and data
|
||||
QStringList keyList = config.keys();
|
||||
QStringList newValueList;
|
||||
for(const auto key : keyList)
|
||||
{
|
||||
if(config[key].isObject())
|
||||
{
|
||||
newValueList << QString(QJsonDocument(config[key].toObject()).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
else if(config[key].isArray())
|
||||
{
|
||||
newValueList << QString(QJsonDocument(config[key].toArray()).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
}
|
||||
|
||||
// compare database data with new data to emit/save changes accordingly
|
||||
for(const auto key : keyList)
|
||||
{
|
||||
QString data = newValueList.takeFirst();
|
||||
if(_sTable->getSettingsRecordString(key) != data)
|
||||
{
|
||||
_sTable->createSettingsRecord(key, data);
|
||||
|
||||
emit settingsChanged(settings::stringToType(key), QJsonDocument::fromJson(data.toLocal8Bit()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@@ -79,6 +79,10 @@
|
||||
{
|
||||
"$ref": "schema-instCapture.json"
|
||||
},
|
||||
"network":
|
||||
{
|
||||
"$ref": "schema-network.json"
|
||||
},
|
||||
"ledConfig":
|
||||
{
|
||||
"$ref": "schema-ledConfig.json"
|
||||
|
@@ -23,5 +23,6 @@
|
||||
<file alias="schema-ledConfig.json">schema/schema-ledConfig.json</file>
|
||||
<file alias="schema-leds.json">schema/schema-leds.json</file>
|
||||
<file alias="schema-instCapture.json">schema/schema-instCapture.json</file>
|
||||
<file alias="schema-network.json">schema/schema-network.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
54
libsrc/hyperion/schema/schema-network.json
Normal file
54
libsrc/hyperion/schema/schema-network.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"title" : "edt_conf_net_heading_title",
|
||||
"required" : true,
|
||||
"properties" :
|
||||
{
|
||||
"internetAccessAPI" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_net_internetAccessAPI_title",
|
||||
"required" : true,
|
||||
"default" : false,
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"ipWhitelist" :
|
||||
{
|
||||
"type" : "array",
|
||||
"title" : "edt_conf_net_ipWhitelist_title",
|
||||
"required" : true,
|
||||
"items" : {
|
||||
"type": "string",
|
||||
"title" : "edt_conf_net_ip_itemtitle"
|
||||
},
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"internetAccessAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"apiAuth" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_net_apiAuth_title",
|
||||
"required" : true,
|
||||
"default" : true,
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"localApiAuth" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_net_localApiAuth_title",
|
||||
"required" : true,
|
||||
"default" : false,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"apiAuth": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 4
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
Reference in New Issue
Block a user