mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Feat: Add Admin API (#617)
* Push progress
TODO: rework RESET, probably to main.cpp again
* resetPassword rework
* enable administration restriction
* add short cmd for userdata
* Js apis
* Refactor JsonCB class
* Add userToken Auth
* Feat: Close connection if ext clients when def pw is set
* Feat: Protect db against pw/token tests
* WebUi PW Support (#9)
* Initial WebUi Password Support
* Small changes
* Initial WebUi Password Support
* Small changes
* Basic WebUi Token support
* added "removeStorage", added uiLock, updated login page
* Small improvments
* Small change
* Fix: prevent downgrade of authorization
* Add translation for localAdminAuth
* Feat: Show always save button in led layout
* Revert "Feat: Show always save button in led layout"
This reverts commit caad1dfcde
.
* Feat: Password change link in notification
* Fix: body padding modal overlap
* Feat: Add instance index to response on switch
* prevent schema error
Signed-off-by: Paulchen-Panther <Paulchen-Panter@protonmail.com>
* Feat: add pw save
* Feat: callout settings/pw replaced with notification
This commit is contained in:
@@ -10,19 +10,19 @@
|
||||
"subcommand" : {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"enum" : ["requestToken","createToken","deleteToken","getTokenList","logout","login","required","answerRequest","getPendingRequests"]
|
||||
"enum" : ["requestToken","createToken","deleteToken","getTokenList","logout","login","required","adminRequired","newPasswordRequired","newPassword","answerRequest","getPendingRequests"]
|
||||
},
|
||||
"tan" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"minLength" : 3
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength" : 8
|
||||
},
|
||||
"newPassword": {
|
||||
"type": "string",
|
||||
"minLength" : 8
|
||||
},
|
||||
"token": {
|
||||
"type": "string",
|
||||
"minLength" : 36
|
||||
|
@@ -48,19 +48,37 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, const bool& localConnection,
|
||||
, _noListener(noListener)
|
||||
, _peerAddress(peerAddress)
|
||||
, _log(log)
|
||||
, _localConnection(localConnection)
|
||||
, _instanceManager(HyperionIManager::getInstance())
|
||||
, _hyperion(nullptr)
|
||||
, _jsonCB(nullptr)
|
||||
, _jsonCB(new JsonCB(this))
|
||||
, _streaming_logging_activated(false)
|
||||
, _imageStreamTimer(new QTimer(this))
|
||||
, _ledStreamTimer(new QTimer(this))
|
||||
{
|
||||
Q_INIT_RESOURCE(JSONRPC_schemas);
|
||||
}
|
||||
|
||||
void JsonAPI::initialize(void)
|
||||
{
|
||||
// For security we block external connections if default PW is set
|
||||
if(!_localConnection && _authManager->hasHyperionDefaultPw())
|
||||
{
|
||||
emit forceClose();
|
||||
}
|
||||
// if this is localConnection and network allows unauth locals, set authorized flag
|
||||
if(_apiAuthRequired && localConnection)
|
||||
if(_apiAuthRequired && _localConnection)
|
||||
_authorized = !_authManager->isLocalAuthRequired();
|
||||
|
||||
// admin access is allowed, when the connection is local and the option for local admin isn't set. Con: All local connections get full access
|
||||
if(_localConnection)
|
||||
{
|
||||
_userAuthorized = !_authManager->isLocalAdminAuthRequired();
|
||||
// just in positive direction
|
||||
if(_userAuthorized)
|
||||
_authorized = true;
|
||||
}
|
||||
|
||||
// setup auth interface
|
||||
connect(_authManager, &AuthManager::newPendingTokenRequest, this, &JsonAPI::handlePendingTokenRequest);
|
||||
connect(_authManager, &AuthManager::tokenResponse, this, &JsonAPI::handleTokenResponse);
|
||||
@@ -68,6 +86,9 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, const bool& localConnection,
|
||||
// listen for killed instances
|
||||
connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &JsonAPI::handleInstanceStateChange);
|
||||
|
||||
// pipe callbacks from subscriptions to parent
|
||||
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
|
||||
|
||||
// init Hyperion pointer
|
||||
handleInstanceSwitch(0);
|
||||
|
||||
@@ -91,30 +112,8 @@ bool JsonAPI::handleInstanceSwitch(const quint8& inst, const bool& forced)
|
||||
// get new Hyperion pointer
|
||||
_hyperion = _instanceManager->getHyperionInstance(inst);
|
||||
|
||||
// the JsonCB creates json messages you can subscribe to e.g. data change events; forward them to the parent client
|
||||
QStringList cbCmds;
|
||||
if(_jsonCB != nullptr)
|
||||
{
|
||||
cbCmds = _jsonCB->getSubscribedCommands();
|
||||
delete _jsonCB;
|
||||
}
|
||||
|
||||
_jsonCB = new JsonCB(_hyperion, this);
|
||||
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
|
||||
|
||||
// read subs
|
||||
for(const auto & entry : cbCmds)
|
||||
{
|
||||
_jsonCB->subscribeFor(entry);
|
||||
}
|
||||
|
||||
// // imageStream last state
|
||||
// if(_ledcolorsImageActive)
|
||||
// connect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage, Qt::UniqueConnection);
|
||||
//
|
||||
// //ledColor stream last state
|
||||
// if(_ledcolorsLedsActive)
|
||||
// connect(_hyperion, &Hyperion::rawLedColors, this, &JsonAPI::streamLedcolorsUpdate, Qt::UniqueConnection);
|
||||
// the JsonCB creates json messages you can subscribe to e.g. data change events
|
||||
_jsonCB->setSubscriptionsTo(_hyperion);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -727,7 +726,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
|
||||
for(const auto & entry : subsArr)
|
||||
{
|
||||
// config callbacks just if auth is set
|
||||
if(entry == "settings-update" && !_authorized)
|
||||
if(entry == "settings-update" && !_userAuthorized)
|
||||
continue;
|
||||
|
||||
if(!_jsonCB->subscribeFor(entry.toString()))
|
||||
@@ -887,17 +886,28 @@ void JsonAPI::handleConfigCommand(const QJsonObject& message, const QString& com
|
||||
}
|
||||
else if (subcommand == "setconfig")
|
||||
{
|
||||
handleConfigSetCommand(message, full_command, tan);
|
||||
if(_userAuthorized)
|
||||
handleConfigSetCommand(message, full_command, tan);
|
||||
else
|
||||
sendErrorReply("No Authorization",command, tan);
|
||||
}
|
||||
else if (subcommand == "getconfig")
|
||||
{
|
||||
sendSuccessDataReply(QJsonDocument(_hyperion->getQJsonConfig()), full_command, tan);
|
||||
if(_userAuthorized)
|
||||
sendSuccessDataReply(QJsonDocument(_hyperion->getQJsonConfig()), full_command, tan);
|
||||
else
|
||||
sendErrorReply("No Authorization",command, tan);
|
||||
}
|
||||
else if (subcommand == "reload")
|
||||
{
|
||||
_hyperion->freeObjects(true);
|
||||
Process::restartHyperion();
|
||||
sendErrorReply("failed to restart hyperion", full_command, tan);
|
||||
if(_userAuthorized)
|
||||
{
|
||||
_hyperion->freeObjects(true);
|
||||
Process::restartHyperion();
|
||||
sendErrorReply("failed to restart hyperion", full_command, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization",command, tan);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1139,11 +1149,33 @@ void JsonAPI::handleVideoModeCommand(const QJsonObject& message, const QString &
|
||||
void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString &command, const int tan)
|
||||
{
|
||||
const QString& subc = message["subcommand"].toString().trimmed();
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
const QString& password = message["password"].toString().trimmed();
|
||||
const QString& newPassword = message["newPassword"].toString().trimmed();
|
||||
|
||||
// catch test if auth is required
|
||||
if(subc == "required")
|
||||
{
|
||||
QJsonObject req;
|
||||
req["required"] = _apiAuthRequired;
|
||||
req["required"] = !_authorized;
|
||||
sendSuccessDataReply(QJsonDocument(req), command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// catch test if admin auth is required
|
||||
if(subc == "adminRequired")
|
||||
{
|
||||
QJsonObject req;
|
||||
req["adminRequired"] = !_userAuthorized;
|
||||
sendSuccessDataReply(QJsonDocument(req), command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// default hyperion password is a security risk, replace it asap
|
||||
if(subc == "newPasswordRequired")
|
||||
{
|
||||
QJsonObject req;
|
||||
req["newPasswordRequired"] = _authManager->hasHyperionDefaultPw();
|
||||
sendSuccessDataReply(QJsonDocument(req), command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
@@ -1153,10 +1185,30 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
{
|
||||
_authorized = false;
|
||||
_userAuthorized = false;
|
||||
// disconnect all kind of data callbacks
|
||||
stopDataConnections();
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// change password
|
||||
if(subc == "newPassword")
|
||||
{
|
||||
// use password, newPassword
|
||||
if(_userAuthorized)
|
||||
{
|
||||
if(_authManager->updateUserPassword("Hyperion", password, newPassword))
|
||||
{
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
sendErrorReply("Failed to update user password",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
sendErrorReply("No Authorization",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// token created from ui
|
||||
if(subc == "createToken")
|
||||
{
|
||||
@@ -1180,11 +1232,11 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
// delete token
|
||||
if(subc == "deleteToken")
|
||||
{
|
||||
const QString& did = message["id"].toString().trimmed();
|
||||
// use id
|
||||
// for user authorized sessions
|
||||
if(_userAuthorized)
|
||||
{
|
||||
_authManager->deleteToken(did);
|
||||
_authManager->deleteToken(id);
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
@@ -1195,8 +1247,8 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
// catch token request
|
||||
if(subc == "requestToken")
|
||||
{
|
||||
// use id
|
||||
const QString& comment = message["comment"].toString().trimmed();
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
_authManager->setNewTokenRequest(this, comment, id);
|
||||
// client should wait for answer
|
||||
return;
|
||||
@@ -1228,7 +1280,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
// accept/deny token request
|
||||
if(subc == "answerRequest")
|
||||
{
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
// use id
|
||||
const bool& accept = message["accept"].toBool(false);
|
||||
if(_userAuthorized)
|
||||
{
|
||||
@@ -1245,7 +1297,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
// deny token request
|
||||
if(subc == "acceptRequest")
|
||||
{
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
// use id
|
||||
if(_userAuthorized)
|
||||
{
|
||||
_authManager->acceptTokenRequest(id);
|
||||
@@ -1256,7 +1308,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
return;
|
||||
}
|
||||
|
||||
// cath get token list
|
||||
// catch get token list
|
||||
if(subc == "getTokenList")
|
||||
{
|
||||
if(_userAuthorized)
|
||||
@@ -1283,12 +1335,27 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
// login
|
||||
if(subc == "login")
|
||||
{
|
||||
// catch token auth
|
||||
const QString& token = message["token"].toString().trimmed();
|
||||
|
||||
// catch token
|
||||
if(!token.isEmpty())
|
||||
{
|
||||
if(token.count() >= 36)
|
||||
// userToken is longer
|
||||
if(token.count() > 36)
|
||||
{
|
||||
if(_authManager->isUserTokenAuthorized("Hyperion",token))
|
||||
{
|
||||
_authorized = true;
|
||||
_userAuthorized = true;
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization", command+"-"+subc, tan);
|
||||
|
||||
return;
|
||||
}
|
||||
// usual app token is 36
|
||||
if(token.count() == 36)
|
||||
{
|
||||
if(_authManager->isTokenAuthorized(token))
|
||||
{
|
||||
@@ -1304,23 +1371,25 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
return;
|
||||
}
|
||||
|
||||
// user & password
|
||||
const QString& user = message["username"].toString().trimmed();
|
||||
const QString& password = message["password"].toString().trimmed();
|
||||
// password
|
||||
// use password
|
||||
|
||||
if(user.count() >= 3 && password.count() >= 8)
|
||||
if(password.count() >= 8)
|
||||
{
|
||||
if(_authManager->isUserAuthorized(user, password))
|
||||
if(_authManager->isUserAuthorized("Hyperion", password))
|
||||
{
|
||||
_authorized = true;
|
||||
_userAuthorized = true;
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
// Return the current valid Hyperion user token
|
||||
QJsonObject obj;
|
||||
obj["token"] = _authManager->getUserToken();
|
||||
sendSuccessDataReply(QJsonDocument(obj),command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization", command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("User or password string too short", command+"-"+subc, tan);
|
||||
sendErrorReply("Password string too short", command+"-"+subc, tan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1343,8 +1412,11 @@ void JsonAPI::handleInstanceCommand(const QJsonObject & message, const QString &
|
||||
|
||||
if(subc == "switchTo")
|
||||
{
|
||||
if(handleInstanceSwitch(inst))
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
if(handleInstanceSwitch(inst)){
|
||||
QJsonObject obj;
|
||||
obj["instance"] = inst;
|
||||
sendSuccessDataReply(QJsonDocument(obj),command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("Selected Hyperion instance isn't running",command+"-"+subc, tan);
|
||||
return;
|
||||
@@ -1576,3 +1648,13 @@ void JsonAPI::handleInstanceStateChange(const instanceState& state, const quint8
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonAPI::stopDataConnections(void)
|
||||
{
|
||||
LoggerManager::getInstance()->disconnect();
|
||||
_streaming_logging_activated = false;
|
||||
_jsonCB->resetSubscriptions();
|
||||
_imageStreamTimer->stop();
|
||||
_ledStreamTimer->stop();
|
||||
|
||||
}
|
||||
|
@@ -27,87 +27,138 @@
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
JsonCB::JsonCB(Hyperion* hyperion, QObject* parent)
|
||||
JsonCB::JsonCB(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _hyperion(hyperion)
|
||||
, _componentRegister(& _hyperion->getComponentRegister())
|
||||
, _hyperion(nullptr)
|
||||
, _componentRegister(nullptr)
|
||||
, _bonjour(BonjourBrowserWrapper::getInstance())
|
||||
, _prioMuxer(_hyperion->getMuxerInstance())
|
||||
, _prioMuxer(nullptr)
|
||||
{
|
||||
_availableCommands << "components-update" << "sessions-update" << "priorities-update" << "imageToLedMapping-update"
|
||||
<< "adjustment-update" << "videomode-update" << "effects-update" << "settings-update" << "leds-update" << "instance-update";
|
||||
}
|
||||
|
||||
bool JsonCB::subscribeFor(const QString& type)
|
||||
bool JsonCB::subscribeFor(const QString& type, const bool & unsubscribe)
|
||||
{
|
||||
if(!_availableCommands.contains(type))
|
||||
return false;
|
||||
|
||||
if(unsubscribe)
|
||||
_subscribedCommands.removeAll(type);
|
||||
else
|
||||
_subscribedCommands << type;
|
||||
|
||||
if(type == "components-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState);
|
||||
else
|
||||
connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "sessions-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_bonjour, &BonjourBrowserWrapper::browserChange, this, &JsonCB::handleBonjourChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_bonjour, &BonjourBrowserWrapper::browserChange, this, &JsonCB::handleBonjourChange);
|
||||
else
|
||||
connect(_bonjour, &BonjourBrowserWrapper::browserChange, this, &JsonCB::handleBonjourChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "priorities-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection);
|
||||
connect(_prioMuxer, &PriorityMuxer::autoSelectChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection);
|
||||
if(unsubscribe){
|
||||
disconnect(_prioMuxer,0 ,0 ,0);
|
||||
} else {
|
||||
connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection);
|
||||
connect(_prioMuxer, &PriorityMuxer::autoSelectChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
if(type == "imageToLedMapping-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "adjustment-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCB::handleAdjustmentChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCB::handleAdjustmentChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCB::handleAdjustmentChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "videomode-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::newVideoMode, this, &JsonCB::handleVideoModeChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::newVideoMode, this, &JsonCB::handleVideoModeChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::newVideoMode, this, &JsonCB::handleVideoModeChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "effects-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCB::handleEffectListChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCB::handleEffectListChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCB::handleEffectListChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "settings-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "leds-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleLedsConfigChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleLedsConfigChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleLedsConfigChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
|
||||
if(type == "instance-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange);
|
||||
else
|
||||
connect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void JsonCB::resetSubscriptions(void){
|
||||
for(const auto & entry : getSubscribedCommands()){
|
||||
subscribeFor(entry, true);
|
||||
}
|
||||
}
|
||||
|
||||
void JsonCB::setSubscriptionsTo(Hyperion* hyperion){
|
||||
// get current subs
|
||||
QStringList currSubs(getSubscribedCommands());
|
||||
|
||||
// stop subs
|
||||
resetSubscriptions();
|
||||
|
||||
// update pointer
|
||||
_hyperion = hyperion;
|
||||
_componentRegister = &_hyperion->getComponentRegister();
|
||||
_prioMuxer = _hyperion->getMuxerInstance();
|
||||
|
||||
// re-apply subs
|
||||
for(const auto & entry : currSubs)
|
||||
{
|
||||
subscribeFor(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void JsonCB::doCallback(const QString& cmd, const QVariant& data)
|
||||
{
|
||||
QJsonObject obj;
|
||||
|
@@ -12,14 +12,16 @@ AuthManager* AuthManager::manager = nullptr;
|
||||
|
||||
AuthManager::AuthManager(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _authTable(new AuthTable(this))
|
||||
, _authTable(new AuthTable("",this))
|
||||
, _metaTable(new MetaTable(this))
|
||||
, _pendingRequests()
|
||||
, _authRequired(true)
|
||||
, _timer(new QTimer(this))
|
||||
, _authBlockTimer(new QTimer(this))
|
||||
{
|
||||
AuthManager::manager = this;
|
||||
|
||||
|
||||
// get uuid
|
||||
_uuid = _metaTable->getUUID();
|
||||
|
||||
@@ -27,21 +29,18 @@ AuthManager::AuthManager(QObject* parent)
|
||||
_timer->setInterval(1000);
|
||||
connect(_timer, &QTimer::timeout, this, &AuthManager::checkTimeout);
|
||||
|
||||
// setup authBlockTimer
|
||||
_authBlockTimer->setInterval(60000);
|
||||
connect(_authBlockTimer, &QTimer::timeout, this, &AuthManager::checkAuthBlockTimeout);
|
||||
|
||||
// init with default user and password
|
||||
if(!_authTable->userExist("Hyperion"))
|
||||
{
|
||||
_authTable->createUser("Hyperion","hyperion");
|
||||
}
|
||||
}
|
||||
|
||||
bool & AuthManager::isAuthRequired()
|
||||
{
|
||||
return _authRequired;
|
||||
}
|
||||
|
||||
bool & AuthManager::isLocalAuthRequired()
|
||||
{
|
||||
return _localAuthRequired;
|
||||
// update Hyperion user token on startup
|
||||
_authTable->setUserToken("Hyperion");
|
||||
}
|
||||
|
||||
const AuthManager::AuthDefinition AuthManager::createToken(const QString& comment)
|
||||
@@ -77,14 +76,69 @@ const QVector<AuthManager::AuthDefinition> AuthManager::getTokenList()
|
||||
return finalVec;
|
||||
}
|
||||
|
||||
const QString AuthManager::getUserToken(const QString & usr)
|
||||
{
|
||||
return QString(_authTable->getUserToken(usr));
|
||||
}
|
||||
|
||||
void AuthManager::setAuthBlock(const bool& user)
|
||||
{
|
||||
// current timestamp +10 minutes
|
||||
if(user)
|
||||
_userAuthAttempts.append(QDateTime::currentMSecsSinceEpoch()+600000);
|
||||
else
|
||||
_tokenAuthAttempts.append(QDateTime::currentMSecsSinceEpoch()+600000);
|
||||
|
||||
QMetaObject::invokeMethod(_authBlockTimer, "start", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
bool AuthManager::isUserAuthorized(const QString& user, const QString& pw)
|
||||
{
|
||||
return _authTable->isUserAuthorized(user, pw);
|
||||
if(isUserAuthBlocked())
|
||||
return false;
|
||||
|
||||
if(!_authTable->isUserAuthorized(user, pw)){
|
||||
setAuthBlock(true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AuthManager::isTokenAuthorized(const QString& token)
|
||||
{
|
||||
return _authTable->tokenExist(token);
|
||||
if(isTokenAuthBlocked())
|
||||
return false;
|
||||
|
||||
if(!_authTable->tokenExist(token)){
|
||||
setAuthBlock();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AuthManager::isUserTokenAuthorized(const QString& usr, const QString& token)
|
||||
{
|
||||
if(isUserAuthBlocked())
|
||||
return false;
|
||||
|
||||
if(!_authTable->isUserTokenAuthorized(usr, token)){
|
||||
setAuthBlock(true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AuthManager::updateUserPassword(const QString& user, const QString& pw, const QString& newPw)
|
||||
{
|
||||
if(isUserAuthorized(user, pw))
|
||||
return _authTable->updateUserPassword(user, newPw);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AuthManager::resetHyperionUser()
|
||||
{
|
||||
return _authTable->resetHyperionUser();
|
||||
}
|
||||
|
||||
void AuthManager::setNewTokenRequest(QObject* caller, const QString& comment, const QString& id)
|
||||
@@ -144,6 +198,7 @@ void AuthManager::handleSettingsUpdate(const settings::type& type, const QJsonDo
|
||||
const QJsonObject& obj = config.object();
|
||||
_authRequired = obj["apiAuth"].toBool(true);
|
||||
_localAuthRequired = obj["localApiAuth"].toBool(false);
|
||||
_localAdminAuthRequired = obj["localAdminAuth"].toBool(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,3 +222,25 @@ void AuthManager::checkTimeout()
|
||||
if(_pendingRequests.isEmpty())
|
||||
_timer->stop();
|
||||
}
|
||||
|
||||
void AuthManager::checkAuthBlockTimeout(){
|
||||
// handle user auth block
|
||||
for (auto it = _userAuthAttempts.begin(); it != _userAuthAttempts.end(); it++) {
|
||||
// after 10 minutes, we remove the entry
|
||||
if (*it < (uint64_t)QDateTime::currentMSecsSinceEpoch()) {
|
||||
_userAuthAttempts.erase(it--);
|
||||
}
|
||||
}
|
||||
|
||||
// handle token auth block
|
||||
for (auto it = _tokenAuthAttempts.begin(); it != _tokenAuthAttempts.end(); it++) {
|
||||
// after 10 minutes, we remove the entry
|
||||
if (*it < (uint64_t)QDateTime::currentMSecsSinceEpoch()) {
|
||||
_tokenAuthAttempts.erase(it--);
|
||||
}
|
||||
}
|
||||
|
||||
// if the lists are empty we stop
|
||||
if(_userAuthAttempts.empty() && _tokenAuthAttempts.empty())
|
||||
_authBlockTimer->stop();
|
||||
}
|
||||
|
@@ -66,6 +66,14 @@
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 5
|
||||
},
|
||||
"localAdminAuth" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_net_localAdminAuth_title",
|
||||
"required" : true,
|
||||
"default" : true,
|
||||
"propertyOrder" : 5
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
|
@@ -17,7 +17,10 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket, const bool& local
|
||||
// create a new instance of JsonAPI
|
||||
_jsonAPI = new JsonAPI(socket->peerAddress().toString(), _log, localConnection, this);
|
||||
// get the callback messages from JsonAPI and send it to the client
|
||||
connect(_jsonAPI,SIGNAL(callbackMessage(QJsonObject)),this,SLOT(sendMessage(QJsonObject)));
|
||||
connect(_jsonAPI, &JsonAPI::callbackMessage, this , &JsonClientConnection::sendMessage);
|
||||
connect(_jsonAPI, &JsonAPI::forceClose, this , [&](){ _socket->close(); } );
|
||||
|
||||
_jsonAPI->initialize();
|
||||
}
|
||||
|
||||
void JsonClientConnection::readRequest()
|
||||
|
@@ -300,8 +300,8 @@ void QtHttpClientWrapper::onReplySendDataRequested (void)
|
||||
|
||||
void QtHttpClientWrapper::sendToClientWithReply(QtHttpReply * reply)
|
||||
{
|
||||
connect (reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested);
|
||||
connect (reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested);
|
||||
connect (reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested, Qt::UniqueConnection);
|
||||
connect (reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested, Qt::UniqueConnection);
|
||||
m_parsingStatus = sendReplyToClient (reply);
|
||||
}
|
||||
|
||||
@@ -340,3 +340,19 @@ QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHtt
|
||||
|
||||
return AwaitingRequest;
|
||||
}
|
||||
|
||||
void QtHttpClientWrapper::closeConnection()
|
||||
{
|
||||
// probably filter for request to follow http spec
|
||||
if(m_currentRequest != Q_NULLPTR)
|
||||
{
|
||||
QtHttpReply reply(m_serverHandle);
|
||||
reply.setStatusCode(QtHttpReply::StatusCode::Forbidden);
|
||||
|
||||
connect (&reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested, Qt::UniqueConnection);
|
||||
connect (&reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested, Qt::UniqueConnection);
|
||||
|
||||
m_parsingStatus = sendReplyToClient(&reply);
|
||||
}
|
||||
m_sockClient->close ();
|
||||
}
|
||||
|
@@ -34,6 +34,11 @@ public:
|
||||
/// @brief Wrapper for sendReplyToClient(), handles m_parsingStatus and signal connect
|
||||
void sendToClientWithReply (QtHttpReply * reply);
|
||||
|
||||
///
|
||||
/// @brief close a connection with FORBIDDEN header (used from JsonAPI over HTTP)
|
||||
///
|
||||
void closeConnection();
|
||||
|
||||
private slots:
|
||||
void onClientDataReceived (void);
|
||||
|
||||
|
@@ -15,14 +15,20 @@ WebJsonRpc::WebJsonRpc(QtHttpRequest* request, QtHttpServer* server, const bool&
|
||||
const QString client = request->getClientInfo().clientAddress.toString();
|
||||
_jsonAPI = new JsonAPI(client, _log, localConnection, this, true);
|
||||
connect(_jsonAPI, &JsonAPI::callbackMessage, this, &WebJsonRpc::handleCallback);
|
||||
connect(_jsonAPI, &JsonAPI::forceClose, [&]() { _wrapper->closeConnection(); _stopHandle = true; });
|
||||
_jsonAPI->initialize();
|
||||
}
|
||||
|
||||
void WebJsonRpc::handleMessage(QtHttpRequest* request)
|
||||
{
|
||||
QByteArray header = request->getHeader("Authorization");
|
||||
QByteArray data = request->getRawData();
|
||||
_unlocked = true;
|
||||
_jsonAPI->handleMessage(data,header);
|
||||
// TODO better solution. If jsonAPI emits forceClose the request is deleted and the following call to this method results in segfault
|
||||
if(!_stopHandle)
|
||||
{
|
||||
QByteArray header = request->getHeader("Authorization");
|
||||
QByteArray data = request->getRawData();
|
||||
_unlocked = true;
|
||||
_jsonAPI->handleMessage(data,header);
|
||||
}
|
||||
}
|
||||
|
||||
void WebJsonRpc::handleCallback(QJsonObject obj)
|
||||
|
@@ -22,6 +22,7 @@ private:
|
||||
Logger* _log;
|
||||
JsonAPI* _jsonAPI;
|
||||
|
||||
bool _stopHandle = false;
|
||||
bool _unlocked = false;
|
||||
|
||||
private slots:
|
||||
|
@@ -25,6 +25,7 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, const
|
||||
// Json processor
|
||||
_jsonAPI = new JsonAPI(client, _log, localConnection, this);
|
||||
connect(_jsonAPI, &JsonAPI::callbackMessage, this, &WebSocketClient::sendMessage);
|
||||
connect(_jsonAPI, &JsonAPI::forceClose, this,[this]() { this->sendClose(CLOSECODE::NORMAL); });
|
||||
|
||||
Debug(_log, "New connection from %s", QSTRING_CSTR(client));
|
||||
|
||||
@@ -40,6 +41,9 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, const
|
||||
|
||||
_socket->write(QSTRING_CSTR(data), data.size());
|
||||
_socket->flush();
|
||||
|
||||
// Init JsonAPI
|
||||
_jsonAPI->initialize();
|
||||
}
|
||||
|
||||
void WebSocketClient::handleWebSocketFrame(void)
|
||||
@@ -123,7 +127,7 @@ void WebSocketClient::handleWebSocketFrame(void)
|
||||
handleBinaryMessage(_wsReceiveBuffer);
|
||||
}
|
||||
_wsReceiveBuffer.clear();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
Reference in New Issue
Block a user