refactor: API split (#721)

* refactor: API split

* refactor: cleanup hyperiond
This commit is contained in:
brindosch 2020-03-26 17:59:41 +01:00 committed by GitHub
parent c6c6453267
commit 2739aec1e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 2044 additions and 1128 deletions

21
.editorconfig Normal file
View File

@ -0,0 +1,21 @@
root = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true
# Tab indentation (no size specified)
[Makefile]
indent_style = tab
[*.{c,h,cpp,hpp}]
indent_style = tab
indent_size = 4
# js and webui
[*.{ts,js,html}]
indent_style = space
indent_size = 2

View File

@ -49,6 +49,8 @@
"general_btn_next": "Weiter",
"general_btn_back": "Zurück",
"general_btn_iswitch": "Switch",
"general_btn_grantAccess": "Zugriff gewähren",
"general_btn_denyAccess": "Zugriff ablehnen",
"general_chars_needed": "weitere Zeichen benötigt",
"general_wiki_moreto": "Mehr Informationen zu \"$1\" findest du in unserem Wiki",
"dashboard_label_intro": "Das Dashboard zeigt dir Informationen zum Systemstatus, ob Updates verfügbar sind, den Komponentenstatus sowie die letzten Blog-Posts vom Hyperion Team.",
@ -203,6 +205,8 @@
"conf_network_tok_diaTitle" : "Neues Token erstellt!",
"conf_network_tok_diaMsg" : "Hier ist dein neues Token, welches für den Zugriff auf die Hyperion API verwendet werden kann. Aus Sicherheitsgründen können Tokens nach der Erstellung nur einmalig eingesehen werden, notiere es dir daher jetzt.",
"conf_network_tok_intro" : "Hier kannst du Token zur API Authentifizierung erstellen oder löschen. Neu erstellte Token werden einmalig angezeigt.",
"conf_network_tok_grantT": "App Token angefordert",
"conf_network_tok_grantMsg": "Eine App fordert Zugriff auf die Hyperion API durch ein Token. Möchtest du dies zulassen? Bitte überprüfe die angegebenen Informationen!",
"conf_logging_label_intro": "Überprüfe die Meldungen im Prokotoll um zu erfahren was Hyperion gerade beschäftigt. Je nach eingestellter Protokoll-Stufe siehst du mehr oder weniger Informationen.",
"conf_logging_btn_pbupload": "Bericht für Supportanfrage hochladen",
"conf_logging_btn_autoscroll": "Automatisch scrollen",

View File

@ -48,6 +48,8 @@
"general_btn_next" : "Next",
"general_btn_back" : "Back",
"general_btn_iswitch" : "Switch",
"general_btn_grantAccess": "Grant Access",
"general_btn_denyAccess": "Deny Access",
"general_chars_needed": "more characters needed",
"general_wiki_moreto" : "More information about \"$1\" is available at our",
"dashboard_label_intro" : "This dashboard gives you a quick overview of your Hyperion installation and shows you the latest news from the Hyperion Blog.",
@ -202,6 +204,8 @@
"conf_network_tok_diaTitle" : "New Token created!",
"conf_network_tok_diaMsg" : "Here is your new token which can be used to grant an application access to the Hyperion API. For security reasons you can't view it again so use/note it down.",
"conf_network_tok_intro" : "Here you can create and delete tokens for API authentication. Created tokens will only be displayed once.",
"conf_network_tok_grantT": "App Requests Token",
"conf_network_tok_grantMsg": "An App requested a token to get access to the Hyperion API. Do you want to grant access? Please verify the provided information!",
"conf_logging_label_intro" : "Area to check log messages, you will see more or less information depending on the set logging level .",
"conf_logging_btn_pbupload" : "Upload a report for support requests",
"conf_logging_btn_autoscroll" : "Auto scrolling",

View File

@ -1,22 +1,21 @@
var instNameInit = false
$(document).ready( function() {
$(document).ready(function () {
loadContentTo("#container_connection_lost","connection_lost");
loadContentTo("#container_restart","restart");
loadContentTo("#container_connection_lost", "connection_lost");
loadContentTo("#container_restart", "restart");
initWebSocket();
$(window.hyperion).on("cmd-serverinfo",function(event){
$(window.hyperion).on("cmd-serverinfo", function (event) {
window.serverInfo = event.response.info;
// comps
window.comps = event.response.info.components
window.comps = event.response.info.components
$(window.hyperion).trigger("ready");
window.comps.forEach( function(obj) {
if (obj.name == "ALL")
{
if(obj.enabled)
window.comps.forEach(function (obj) {
if (obj.name == "ALL") {
if (obj.enabled)
$("#hyperion_disabled_notify").fadeOut("fast");
else
$("#hyperion_disabled_notify").fadeIn("fast");
@ -25,14 +24,13 @@ $(document).ready( function() {
// determine button visibility
var running = window.serverInfo.instance.filter(entry => entry.running);
if(running.length > 1)
if (running.length > 1)
$('#btn_hypinstanceswitch').toggle(true)
else
$('#btn_hypinstanceswitch').toggle(false)
// update listing at button
updateHyperionInstanceListing()
if(!instNameInit)
{
if (!instNameInit) {
window.currentHyperionInstanceName = getInstanceNameByIndex(0);
instNameInit = true;
}
@ -40,17 +38,40 @@ $(document).ready( function() {
updateSessions();
}); // end cmd-serverinfo
$(window.hyperion).on("cmd-sessions-update", function(event) {
$(window.hyperion).on("cmd-sessions-update", function (event) {
window.serverInfo.sessions = event.response.data;
updateSessions();
});
$(window.hyperion).one("cmd-authorize-getTokenList", function(event) {
$(window.hyperion).on("cmd-authorize-tokenRequest cmd-authorize-getPendingTokenRequests", function (event) {
var val = event.response.info;
if (Array.isArray(event.response.info)) {
if (event.response.info.length == 0) {
return
}
val = event.response.info[0]
if (val.comment == '')
$('#modal_dialog').modal('hide');
}
showInfoDialog("grantToken", $.i18n('conf_network_tok_grantT'), $.i18n('conf_network_tok_grantMsg') + '<br><span style="font-weight:bold">App: ' + val.comment + '</span><br><span style="font-weight:bold">Code: ' + val.id + '</span>')
$("#tok_grant_acc").off().on('click', function () {
tokenList.push(val)
// forward event, in case we need to rebuild the list now
$(window.hyperion).trigger({ type: "build-token-list" });
requestHandleTokenRequest(val.id, true)
});
$("#tok_deny_acc").off().on('click', function () {
requestHandleTokenRequest(val.id, false)
});
});
$(window.hyperion).one("cmd-authorize-getTokenList", function (event) {
tokenList = event.response.info;
requestServerInfo();
});
$(window.hyperion).on("cmd-sysinfo", function(event) {
$(window.hyperion).on("cmd-sysinfo", function (event) {
requestServerInfo();
window.sysInfo = event.response.info;
@ -58,33 +79,34 @@ $(document).ready( function() {
window.currentChannel = window.sysInfo.hyperion.channel;
});
$(window.hyperion).one("cmd-config-getschema", function(event) {
$(window.hyperion).one("cmd-config-getschema", function (event) {
window.serverSchema = event.response.info;
requestServerConfig();
requestTokenInfo();
requestTokenInfo();
requestGetPendingTokenRequests();
window.schema = window.serverSchema.properties;
});
$(window.hyperion).on("cmd-config-getconfig", function(event) {
$(window.hyperion).on("cmd-config-getconfig", function (event) {
window.serverConfig = event.response.info;
requestSysInfo();
window.showOptHelp = window.serverConfig.general.showOptHelp;
});
$(window.hyperion).on("cmd-config-setconfig", function(event) {
if (event.response.success === true) {
$(window.hyperion).on("cmd-config-setconfig", function (event) {
if (event.response.success === true) {
showNotification('success', $.i18n('dashboard_alert_message_confsave_success'), $.i18n('dashboard_alert_message_confsave_success_t'))
}
});
}
});
$(window.hyperion).on("cmd-authorize-login", function(event) {
$(window.hyperion).on("cmd-authorize-login", function (event) {
$("#main-nav").removeAttr('style')
$("#top-navbar").removeAttr('style')
if(window.defaultPasswordIsSet === true)
showNotification('warning', $.i18n('dashboard_message_default_password'), $.i18n('dashboard_message_default_password_t'), '<a style="cursor:pointer" onClick="changePassword()"> '+$.i18n('InfoDialog_changePassword_title')+'</a>')
if (window.defaultPasswordIsSet === true)
showNotification('warning', $.i18n('dashboard_message_default_password'), $.i18n('dashboard_message_default_password_t'), '<a style="cursor:pointer" onClick="changePassword()"> ' + $.i18n('InfoDialog_changePassword_title') + '</a>')
else
//if logged on and pw != default show option to lock ui
$("#btn_lock_ui").removeAttr('style')
@ -96,32 +118,30 @@ $(document).ready( function() {
requestServerConfigSchema();
});
$(window.hyperion).on("cmd-authorize-newPassword", function(event) {
if (event.response.success === true){
showInfoDialog("success",$.i18n('InfoDialog_changePassword_success'));
$(window.hyperion).on("cmd-authorize-newPassword", function (event) {
if (event.response.success === true) {
showInfoDialog("success", $.i18n('InfoDialog_changePassword_success'));
// not necessarily true, but better than nothing
window.defaultPasswordIsSet = false;
}
});
});
$(window.hyperion).on("cmd-authorize-newPasswordRequired", function(event) {
$(window.hyperion).on("cmd-authorize-newPasswordRequired", function (event) {
var loginToken = getStorage("loginToken", true)
if (event.response.info.newPasswordRequired == true)
{
if (event.response.info.newPasswordRequired == true) {
window.defaultPasswordIsSet = true;
if(loginToken)
if (loginToken)
requestTokenAuthorization(loginToken)
else
requestAuthorization('hyperion');
}
else
{
else {
$("#main-nav").attr('style', 'display:none')
$("#top-navbar").attr('style', 'display:none')
if(loginToken)
if (loginToken)
requestTokenAuthorization(loginToken)
else
loadContentTo("#page-content", "login")
@ -129,7 +149,7 @@ $(document).ready( function() {
}
});
$(window.hyperion).on("cmd-authorize-adminRequired", function(event) {
$(window.hyperion).on("cmd-authorize-adminRequired", function (event) {
//Check if a admin login is required.
//If yes: check if default pw is set. If no: go ahead to get server config and render page
if (event.response.info.adminRequired === true)
@ -138,50 +158,47 @@ $(document).ready( function() {
requestServerConfigSchema();
});
$(window.hyperion).on("error",function(event){
$(window.hyperion).on("error", function (event) {
//If we are getting an error "No Authorization" back with a set loginToken we will forward to new Login (Token is expired.
//e.g.: hyperiond was started new in the meantime)
if (event.reason == "No Authorization" && getStorage("loginToken", true))
{
if (event.reason == "No Authorization" && getStorage("loginToken", true)) {
removeStorage("loginToken", true);
requestRequiresAdminAuth();
}
else
{
showInfoDialog("error","Error", event.reason);
else {
showInfoDialog("error", "Error", event.reason);
}
});
$(window.hyperion).on("open",function(event){
$(window.hyperion).on("open", function (event) {
requestRequiresAdminAuth();
});
$(window.hyperion).one("ready", function(event) {
$(window.hyperion).one("ready", function (event) {
loadContent();
});
$(window.hyperion).on("cmd-adjustment-update", function(event) {
$(window.hyperion).on("cmd-adjustment-update", function (event) {
window.serverInfo.adjustment = event.response.data
});
$(window.hyperion).on("cmd-videomode-update", function(event) {
$(window.hyperion).on("cmd-videomode-update", function (event) {
window.serverInfo.videomode = event.response.data.videomode
});
$(window.hyperion).on("cmd-components-update", function(event) {
$(window.hyperion).on("cmd-components-update", function (event) {
let obj = event.response.data
// notfication in index
if (obj.name == "ALL")
{
if(obj.enabled)
if (obj.name == "ALL") {
if (obj.enabled)
$("#hyperion_disabled_notify").fadeOut("fast");
else
$("#hyperion_disabled_notify").fadeIn("fast");
}
window.comps.forEach((entry, index) => {
if (entry.name === obj.name){
if (entry.name === obj.name) {
window.comps[index] = obj;
}
});
@ -189,7 +206,7 @@ $(document).ready( function() {
$(window.hyperion).trigger("components-updated");
});
$(window.hyperion).on("cmd-instance-update", function(event) {
$(window.hyperion).on("cmd-instance-update", function (event) {
window.serverInfo.instance = event.response.data
var avail = event.response.data;
// notify the update
@ -197,16 +214,13 @@ $(document).ready( function() {
// if our current instance is no longer available we are at instance 0 again.
var isInData = false;
for(var key in avail)
{
if(avail[key].instance == currentHyperionInstance && avail[key].running)
{
for (var key in avail) {
if (avail[key].instance == currentHyperionInstance && avail[key].running) {
isInData = true;
}
}
if(!isInData)
{
if (!isInData) {
//Delete Storage information about the last used but now stopped instance
if (getStorage('lastSelectedInstance', false))
removeStorage('lastSelectedInstance', false)
@ -214,14 +228,14 @@ $(document).ready( function() {
currentHyperionInstance = 0;
currentHyperionInstanceName = getInstanceNameByIndex(0);
requestServerConfig();
setTimeout(requestServerInfo,100)
setTimeout(requestTokenInfo,200)
setTimeout(loadContent,300, undefined, true)
setTimeout(requestServerInfo, 100)
setTimeout(requestTokenInfo, 200)
setTimeout(loadContent, 300, undefined, true)
}
// determine button visibility
var running = serverInfo.instance.filter(entry => entry.running);
if(running.length > 1)
if (running.length > 1)
$('#btn_hypinstanceswitch').toggle(true)
else
$('#btn_hypinstanceswitch').toggle(false)
@ -230,27 +244,27 @@ $(document).ready( function() {
updateHyperionInstanceListing()
});
$(window.hyperion).on("cmd-instance-switchTo", function(event){
$(window.hyperion).on("cmd-instance-switchTo", function (event) {
requestServerConfig();
setTimeout(requestServerInfo,200)
setTimeout(requestTokenInfo,400)
setTimeout(loadContent,400, undefined, true)
setTimeout(requestServerInfo, 200)
setTimeout(requestTokenInfo, 400)
setTimeout(loadContent, 400, undefined, true)
});
$(window.hyperion).on("cmd-effects-update", function(event){
$(window.hyperion).on("cmd-effects-update", function (event) {
window.serverInfo.effects = event.response.data.effects
});
$(".mnava").bind('click.menu', function(e){
$(".mnava").bind('click.menu', function (e) {
loadContent(e);
window.scrollTo(0, 0);
});
});
$(function(){
$(function () {
var sidebar = $('#side-menu'); // cache sidebar to a variable for performance
sidebar.delegate('a.inactive','click',function(){
sidebar.delegate('a.inactive', 'click', function () {
sidebar.find('.active').toggleClass('active inactive');
$(this).toggleClass('active inactive');
});
@ -258,5 +272,5 @@ $(function(){
// hotfix body padding when bs modals overlap
$(document.body).on('hide.bs.modal,hidden.bs.modal', function () {
$('body').css('padding-right','0');
$('body').css('padding-right', '0');
});

View File

@ -204,6 +204,10 @@ function requestTokenInfo()
sendToHyperion("authorize","getTokenList","");
}
function requestGetPendingTokenRequests (id, state) {
sendToHyperion("authorize", "getPendingTokenRequests", "");
}
function requestHandleTokenRequest(id, state)
{
sendToHyperion("authorize","answerRequest",'"id":"'+id+'", "accept":'+state);

409
include/api/API.h Normal file
View File

@ -0,0 +1,409 @@
#pragma once
// hyperion includes
#include <utils/Logger.h>
#include <utils/Components.h>
#include <hyperion/Hyperion.h>
#include <hyperion/HyperionIManager.h>
// auth manager
#include <hyperion/AuthManager.h>
#include <utils/ColorRgb.h>
#include <utils/ColorSys.h>
// qt includes
#include <QString>
class QTimer;
class JsonCB;
class HyperionIManager;
const QString NO_AUTH = "No Authorization";
///
/// @brief API for Hyperion to be inherted from a child class with specific protocol implementations
/// Workflow:
/// 1. create the class
/// 2. connect the forceClose signal, as the api might to close the connection for security reasons
/// 3. call Initialize()
/// 4. proceed as usual
///
class API : public QObject
{
Q_OBJECT
public:
#include <api/apiStructs.h>
// workaround Q_ARG std::map template issues
typedef std::map<int, registerData> MapRegister;
typedef QMap<QString, AuthManager::AuthDefinition> MapAuthDefs;
///
/// Constructor
///
///@ param The parent Logger
/// @param localConnection Is this a local network connection? Use utils/NetOrigin to check that
/// @param parent Parent QObject
///
API(Logger *log, const bool &localConnection, QObject *parent);
protected:
///
/// @brief Initialize the API
/// This call is REQUIRED!
///
void init(void);
///
/// @brief Set a single color
/// @param[in] priority The priority of the written color
/// @param[in] ledColor The color to write to the leds
/// @param[in] timeout_ms The time the leds are set to the given color [ms]
/// @param[in] origin The setter
///
void setColor(const int &priority, const std::vector<uint8_t> &ledColors, const int &timeout_ms = -1, const QString &origin = "API", const hyperion::Components &callerComp = hyperion::COMP_INVALID);
///
/// @brief Set a image
/// @param[in] data The command data
/// @param[in] comp The component that should be used
/// @param[out] replyMsg The replyMsg on failure
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// @return True on success
///
bool setImage(ImageCmdData &data, hyperion::Components comp, QString &replyMsg, const hyperion::Components &callerComp = hyperion::COMP_INVALID);
///
/// @brief Clear a priority in the Muxer, if -1 all priorities are cleared
/// @param priority The priority to clear
/// @param replyMsg the message on failure
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// @return True on success
///
bool clearPriority(const int &priority, QString &replyMsg, const hyperion::Components &callerComp = hyperion::COMP_INVALID);
///
/// @brief Set a new component state
/// @param comp The component name
/// @param compState The new state of the comp
/// @param replyMsg The reply on failure
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// @ return True on success
///
bool setComponentState(const QString &comp, bool &compState, QString &replyMsg, const hyperion::Components &callerComp = hyperion::COMP_INVALID);
///
/// @brief Set a ledToImageMapping type
/// @param type mapping type string
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setLedMappingType(const int &type, const hyperion::Components &callerComp = hyperion::COMP_INVALID);
///
/// @brief Set the 2D/3D modes type
/// @param mode The VideoMode
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setVideoMode(const VideoMode &mode, const hyperion::Components &callerComp = hyperion::COMP_INVALID);
///
/// @brief Set an effect
/// @param dat The effect data
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// REQUIRED dat fields: effectName, priority, duration, origin
///
void setEffect(const EffectCmdData &dat, const hyperion::Components &callerComp = hyperion::COMP_INVALID);
///
/// @brief Set source auto select enabled or disabled
/// @param sate The new state
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setSourceAutoSelect(const bool state, const hyperion::Components &callerComp = hyperion::COMP_INVALID);
///
/// @brief Set the visible priority to given priority
/// @param priority The priority to set
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setVisiblePriority(const int &priority, const hyperion::Components &callerComp = hyperion::COMP_INVALID);
///
/// @brief Register a input or update the meta data of a previous register call
/// ATTENTION: Check unregisterInput() method description !!!
/// @param[in] priority The priority of the channel
/// @param[in] component The component of the channel
/// @param[in] origin Who set the channel (CustomString@IP)
/// @param[in] owner Specific owner string, might be empty
/// @param[in] callerComp The component that call this (e.g. PROTO/FLAT)
///
void registerInput(const int &priority, const hyperion::Components &component, const QString &origin, const QString &owner, const hyperion::Components &callerComp);
///
/// @brief Revoke a registerInput() call by priority. We maintain all registered priorities in this scope
/// ATTENTION: This is MANDATORY if you change (priority change) or stop(clear/timeout) DURING lifetime. If this class destructs it's not needed
/// @param priority The priority to unregister
///
void unregisterInput(const int &priority);
///
/// @brief Handle the instance switching
/// @param inst The requested instance
/// @return True on success else false
///
bool setHyperionInstance(const quint8 &inst);
///
/// @brief Get all contrable components and their state
///
std::map<hyperion::Components, bool> getAllComponents();
///
/// @brief Check if Hyperion ist enabled
/// @return True when enabled else false
///
bool isHyperionEnabled();
///
/// @brief Get all instances data
/// @return The instance data
///
QVector<QVariantMap> getAllInstanceData(void);
///
/// @brief Start instance
/// @param index The instance index
///
void startInstance(const quint8 &index);
///
/// @brief Stop instance
/// @param index The instance index
///
void stopInstance(const quint8 &index);
//////////////////////////////////
/// AUTH / ADMINISTRATION METHODS
//////////////////////////////////
///
/// @brief Delete instance. Requires ADMIN ACCESS
/// @param index The instance index
/// @param replyMsg The reply Msg
/// @return False with reply
///
bool deleteInstance(const quint8 &index, QString &replyMsg);
///
/// @brief Create instance. Requires ADMIN ACCESS
/// @param name With given name
/// @return False with reply
///
QString createInstance(const QString &name);
///
/// @brief Rename an instance. Requires ADMIN ACCESS
/// @param index The instance index
/// @param name With given name
/// @return False with reply
///
QString setInstanceName(const quint8 &index, const QString &name);
///
/// @brief Delete an effect. Requires ADMIN ACCESS
/// @param name The effect name
/// @return True on success else false
///
QString deleteEffect(const QString &name);
///
/// @brief Delete an effect. Requires ADMIN ACCESS
/// @param name The effect name
/// @return True on success else false
///
QString saveEffect(const QJsonObject &data);
///
/// @brief Save settings object. Requires ADMIN ACCESS
/// @param data The data object
///
void saveSettings(const QJsonObject &data);
///
/// @brief Test if we are authorized to use the interface
/// @return The result
///
bool isAuthorized() { return _authorized; };
///
/// @brief Test if we are authorized to use the admin interface
/// @return The result
///
bool isAdminAuthorized() { return _adminAuthorized; };
///
/// @brief Update the Password of Hyperion. Requires ADMIN ACCESS
/// @param password Old password
/// @param newPassword New password
/// @return True on success else false
///
bool updateHyperionPassword(const QString &password, const QString &newPassword);
///
/// @brief Get a new token from AuthManager. Requires ADMIN ACCESS
/// @param comment The comment of the request
/// @param def The final definition
/// @return Empty string on success else error message
///
QString createToken(const QString &comment, AuthManager::AuthDefinition &def);
///
/// @brief Rename a token by given id. Requires ADMIN ACCESS
/// @param id The id of the token
/// @param comment The new comment
/// @return Empty string on success else error message
///
QString renameToken(const QString &id, const QString &comment);
///
/// @brief Delete a token by given id. Requires ADMIN ACCESS
/// @param id The id of the token
/// @return Empty string on success else error message
///
QString deleteToken(const QString &id);
///
/// @brief Set a new token request
/// @param comment The comment
/// @param id The id
///
void setNewTokenRequest(const QString &comment, const QString &id);
///
/// @brief Cancel new token request
/// @param comment The comment
/// @param id The id
///
void cancelNewTokenRequest(const QString &comment, const QString &id);
///
/// @brief Handle a pending token request. Requires ADMIN ACCESS
/// @param id The id fo the request
/// @param accept True when it should be accepted, else false
/// @return True on success
bool handlePendingTokenRequest(const QString &id, const bool accept);
///
/// @brief Get the current List of Tokens. Requires ADMIN ACCESS
/// @param def returns the defintions
/// @return True on success
///
bool getTokenList(QVector<AuthManager::AuthDefinition> &def);
///
/// @brief Get all current pending token requests. Requires ADMIN ACCESS
/// @return True on success
///
bool getPendingTokenRequests(QVector<AuthManager::AuthDefinition> &map);
///
/// @brief Is User Token Authorized. On success this will grant acces to API and ADMIN API
/// @param userToken The user Token
/// @return True on succes
///
bool isUserTokenAuthorized(const QString &userToken);
///
/// @brief Get the current User Token (session token). Requires ADMIN ACCESS
/// @param userToken The user Token
/// @return True on success
///
bool getUserToken(QString &userToken);
///
/// @brief Is a token authrized. On success this will grant acces to the API (NOT ADMIN API)
/// @param token The user Token
/// @return True on succes
///
bool isTokenAuthorized(const QString &token);
///
/// @brief Is User authorized. On success this will grant acces to the API and ADMIN API
/// @param password The password of the User
/// @return True if authorized
///
bool isUserAuthorized(const QString &password);
///
/// @brief Test if Hyperion has the default PW
/// @return The result
///
bool hasHyperionDefaultPw();
///
/// @brief Logout revokes all authorizations
///
void logout();
/// Reflect auth status of this client
bool _authorized;
bool _adminAuthorized;
/// Is this a local connection
bool _localConnection;
AuthManager *_authManager;
HyperionIManager *_instanceManager;
Logger *_log;
Hyperion *_hyperion;
signals:
///
/// @brief The API might decide to block connections for security reasons, this emitter should close the socket
///
void forceClose();
///
/// @brief Emits whenever a new Token request is pending. This signal is just active when ADMIN ACCESS has been granted
/// @param id The id of the request
/// @param comment The comment of the request; If the commen is EMPTY the request has been revoked by the caller. So remove it from the pending list
///
void onPendingTokenRequest(const QString &id, const QString &comment);
///
/// @brief Handle emits from AuthManager of accepted/denied/timeouts token request, just if QObject matches with this instance it will emit.
/// @param success If true the request was accepted else false and no token was created
/// @param token The new token that is now valid
/// @param comment The comment that was part of the request
/// @param id The id that was part of the request
///
void onTokenResponse(const bool &success, const QString &token, const QString &comment, const QString &id);
private slots:
///
/// @brief Is called whenever a Hyperion instance wants the current register list
/// @param callerInstance The instance should be returned in the answer call
///
void requestActiveRegister(QObject *callerInstance);
///
/// @brief See onTokenResponse(). Here we validate the caller instance and on success we will emit onTokenResponse()
/// @param success If true the request was accepted else false and no token was created
/// @param caller The origin caller instance who requested this token
/// @param token The new token that is now valid
/// @param comment The comment that was part of the request
/// @param id The id that was part of the request
///
void checkTokenResponse(const bool &success, QObject *caller, const QString &token, const QString &comment, const QString &id);
private:
void stopDataConnectionss();
// Contains all active register call data
std::map<int, registerData> _activeRegisters;
// current instance index
quint8 _currInstanceIndex;
};

View File

@ -1,7 +1,9 @@
#pragma once
// parent class
#include <api/API.h>
// hyperion includes
#include <utils/Logger.h>
#include <utils/Components.h>
#include <hyperion/Hyperion.h>
#include <hyperion/HyperionIManager.h>
@ -14,7 +16,7 @@ class QTimer;
class JsonCB;
class AuthManager;
class JsonAPI : public QObject
class JsonAPI : public API
{
Q_OBJECT
@ -28,14 +30,14 @@ public:
/// @param localConnection True when the sender has origin home network
/// @param noListener if true, this instance won't listen for hyperion push events
///
JsonAPI(QString peerAddress, Logger* log, const bool& localConnection, QObject* parent, bool noListener = false);
JsonAPI(QString peerAddress, Logger *log, const bool &localConnection, QObject *parent, bool noListener = false);
///
/// Handle an incoming JSON message
///
/// @param message the incoming message as string
///
void handleMessage(const QString & message, const QString& httpAuthHeader = "");
void handleMessage(const QString &message, const QString &httpAuthHeader = "");
///
/// @brief Initialization steps
@ -47,36 +49,35 @@ public slots:
/// @brief Is called whenever the current Hyperion instance pushes new led raw values (if enabled)
/// @param ledColors The current led colors
///
void streamLedcolorsUpdate(const std::vector<ColorRgb>& ledColors);
void streamLedcolorsUpdate(const std::vector<ColorRgb> &ledColors);
///
/// @brief Push images whenever hyperion emits (if enabled)
/// @param image The current image
///
void setImage(const Image<ColorRgb> & image);
void setImage(const Image<ColorRgb> &image);
///
/// @brief Process and push new log messages from logger (if enabled)
///
void incommingLogMessage(const Logger::T_LOG_MESSAGE&);
void incommingLogMessage(const Logger::T_LOG_MESSAGE &);
private slots:
///
/// @brief Handle emits from AuthManager of new request, just _userAuthorized sessions are allowed to handle them
/// @brief Handle emits from API of a new Token request.
/// @param id The id of the request
/// @param The comment which needs to be accepted
///
void handlePendingTokenRequest(const QString& id, const QString& comment);
void newPendingTokenRequest(const QString &id, const QString &comment);
///
/// @brief Handle emits from AuthManager of accepted/denied/timeouts token request, just if QObject matches with this instance we are allowed to send response.
/// @param success If true the request was accepted else false and no token was created
/// @param caller The origin caller instance who requested this token
/// @param token The new token that is now valid
/// @param comment The comment that was part of the request
/// @param id The id that was part of the request
///
void handleTokenResponse(const bool& success, QObject* caller, const QString& token, const QString& comment, const QString& id);
void handleTokenResponse(const bool &success, const QString &token, const QString &comment, const QString &id);
///
/// @brief Handle whenever the state of a instance (HyperionIManager) changes according to enum instanceState
@ -84,7 +85,7 @@ private slots:
/// @param instance The index of instance
/// @param name The name of the instance, just available with H_CREATED
///
void handleInstanceStateChange(const instanceState& state, const quint8& instance, const QString& name = QString());
void handleInstanceStateChange(const instanceState &state, const quint8 &instance, const QString &name = QString());
signals:
///
@ -97,42 +98,15 @@ signals:
///
void forwardJsonMessage(QJsonObject);
///
/// @brief The API might decide to block connections for security reasons, this emitter should close the socket
///
void forceClose();
private:
/// Auth management pointer
AuthManager* _authManager;
/// Reflect auth status of this client
bool _authorized;
bool _userAuthorized;
/// Reflect auth required
bool _apiAuthRequired;
// true if further callbacks are forbidden (http)
bool _noListener;
/// The peer address of the client
QString _peerAddress;
/// Log instance
Logger* _log;
/// Is this a local connection
bool _localConnection;
/// Hyperion instance manager
HyperionIManager* _instanceManager;
/// Hyperion instance
Hyperion* _hyperion;
// The JsonCB instance which handles data subscription/notifications
JsonCB* _jsonCB;
JsonCB *_jsonCB;
// streaming buffers
QJsonObject _streaming_leds_reply;
@ -142,17 +116,8 @@ private:
/// flag to determine state of log streaming
bool _streaming_logging_activated;
/// timer for live video refresh
QTimer* _imageStreamTimer;
/// image stream connection handle
QMetaObject::Connection _imageStreamConnection;
/// the current streaming image
Image<ColorRgb> _currentImage;
/// timer for led color refresh
QTimer* _ledStreamTimer;
QTimer *_ledStreamTimer;
/// led stream connection handle
QMetaObject::Connection _ledStreamConnection;
@ -166,21 +131,21 @@ private:
/// @param forced indicate if it was a forced switch by system
/// @return true on success. false if not found
///
bool handleInstanceSwitch(const quint8& instance = 0, const bool& forced = false);
bool handleInstanceSwitch(const quint8 &instance = 0, const bool &forced = false);
///
/// Handle an incoming JSON Color message
///
/// @param message the incoming message
///
void handleColorCommand(const QJsonObject & message, const QString &command, const int tan);
void handleColorCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON Image message
///
/// @param message the incoming message
///
void handleImageCommand(const QJsonObject & message, const QString &command, const int tan);
void handleImageCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON Effect message
@ -194,126 +159,117 @@ private:
///
/// @param message the incoming message
///
void handleCreateEffectCommand(const QJsonObject & message, const QString &command, const int tan);
void handleCreateEffectCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON Effect message (Delete JSON Effect)
///
/// @param message the incoming message
///
void handleDeleteEffectCommand(const QJsonObject & message, const QString &command, const int tan);
void handleDeleteEffectCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON System info message
///
/// @param message the incoming message
///
void handleSysInfoCommand(const QJsonObject & message, const QString &command, const int tan);
void handleSysInfoCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON Server info message
///
/// @param message the incoming message
///
void handleServerInfoCommand(const QJsonObject & message, const QString &command, const int tan);
void handleServerInfoCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON Clear message
///
/// @param message the incoming message
///
void handleClearCommand(const QJsonObject & message, const QString &command, const int tan);
void handleClearCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON Clearall message
///
/// @param message the incoming message
///
void handleClearallCommand(const QJsonObject & message, const QString &command, const int tan);
void handleClearallCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON Adjustment message
///
/// @param message the incoming message
///
void handleAdjustmentCommand(const QJsonObject & message, const QString &command, const int tan);
void handleAdjustmentCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON SourceSelect message
///
/// @param message the incoming message
///
void handleSourceSelectCommand(const QJsonObject & message, const QString &command, const int tan);
void handleSourceSelectCommand(const QJsonObject &message, const QString &command, const int tan);
/// Handle an incoming JSON GetConfig message and check subcommand
///
/// @param message the incoming message
///
void handleConfigCommand(const QJsonObject & message, const QString &command, const int tan);
void handleConfigCommand(const QJsonObject &message, const QString &command, const int tan);
/// Handle an incoming JSON GetConfig message from handleConfigCommand()
///
/// @param message the incoming message
///
void handleSchemaGetCommand(const QJsonObject & message, const QString &command, const int tan);
void handleSchemaGetCommand(const QJsonObject &message, const QString &command, const int tan);
/// Handle an incoming JSON SetConfig message from handleConfigCommand()
///
/// @param message the incoming message
///
void handleConfigSetCommand(const QJsonObject & message, const QString &command, const int tan);
void handleConfigSetCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON Component State message
///
/// @param message the incoming message
///
void handleComponentStateCommand(const QJsonObject & message, const QString &command, const int tan);
void handleComponentStateCommand(const QJsonObject &message, const QString &command, const int tan);
/// Handle an incoming JSON Led Colors message
///
/// @param message the incoming message
///
void handleLedColorsCommand(const QJsonObject & message, const QString &command, const int tan);
void handleLedColorsCommand(const QJsonObject &message, const QString &command, const int tan);
/// Handle an incoming JSON Logging message
///
/// @param message the incoming message
///
void handleLoggingCommand(const QJsonObject & message, const QString &command, const int tan);
void handleLoggingCommand(const QJsonObject &message, const QString &command, const int tan);
/// Handle an incoming JSON Proccessing message
///
/// @param message the incoming message
///
void handleProcessingCommand(const QJsonObject & message, const QString &command, const int tan);
void handleProcessingCommand(const QJsonObject &message, const QString &command, const int tan);
/// Handle an incoming JSON VideoMode message
///
/// @param message the incoming message
///
void handleVideoModeCommand(const QJsonObject & message, const QString &command, const int tan);
void handleVideoModeCommand(const QJsonObject &message, const QString &command, const int tan);
/// Handle an incoming JSON plugin message
///
/// @param message the incoming message
///
void handleAuthorizeCommand(const QJsonObject & message, const QString &command, const int tan);
///
/// Handle HTTP on-the-fly token authorization
/// @param command The command
/// @param tan The tan
/// @param token The token to verify
/// @return True on succcess else false (pushes failed client feedback)
///
bool handleHTTPAuth(const QString& command, const int& tan, const QString& token);
void handleAuthorizeCommand(const QJsonObject &message, const QString &command, const int tan);
/// Handle an incoming JSON instance message
///
/// @param message the incoming message
///
void handleInstanceCommand(const QJsonObject & message, const QString &command, const int tan);
void handleInstanceCommand(const QJsonObject &message, const QString &command, const int tan);
///
/// Handle an incoming JSON message of unknown type
@ -323,22 +279,22 @@ private:
///
/// Send a standard reply indicating success
///
void sendSuccessReply(const QString &command="", const int tan=0);
void sendSuccessReply(const QString &command = "", const int tan = 0);
///
/// Send a standard reply indicating success with data
///
void sendSuccessDataReply(const QJsonDocument &doc, const QString &command="", const int &tan=0);
void sendSuccessDataReply(const QJsonDocument &doc, const QString &command = "", const int &tan = 0);
///
/// Send an error message back to the client
///
/// @param error String describing the error
///
void sendErrorReply(const QString & error, const QString &command="", const int tan=0);
void sendErrorReply(const QString &error, const QString &command = "", const int tan = 0);
///
/// @brief Kill all signal/slot connections to stop possible data emitter
///
void stopDataConnections(void);
};
};

View File

@ -12,6 +12,8 @@
#include <utils/VideoMode.h>
// settings
#include <utils/settings.h>
// AuthManager
#include <hyperion/AuthManager.h>
class Hyperion;
class ComponentRegister;
@ -119,6 +121,11 @@ private slots:
///
void handleInstanceChange();
///
/// @brief Handle AuthManager token changes
///
void handleTokenChange(const QVector<AuthManager::AuthDefinition> &def);
private:
/// pointer of Hyperion instance
Hyperion* _hyperion;

36
include/api/apiStructs.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <QString>
#include <QByteArray>
struct ImageCmdData
{
int priority;
QString origin;
int64_t duration;
int width;
int height;
int scale;
QString format;
QString imgName;
QByteArray data;
};
struct EffectCmdData
{
int priority;
int duration;
QString pythonScript;
QString origin;
QString effectName;
QString data;
QJsonObject args;
};
struct registerData
{
const hyperion::Components component;
const QString origin;
const QString owner;
const hyperion::Components callerComp;
};

View File

@ -222,6 +222,22 @@ public:
return deleteRecord(cond);
}
///
/// @brief Rename token record by id
/// @param[in] id The token id
/// @param[in] comment The new comment
/// @return true on success else false
///
inline bool renameToken(const QString &id, const QString &comment)
{
QVariantMap map;
map["comment"] = comment;
VectorPair cond;
cond.append(CPair("id", id));
return updateRecord(cond, map);
}
///
/// @brief Get all 'comment', 'last_use' and 'id' column entries
/// @return A vector of all lists

View File

@ -41,19 +41,17 @@ public:
///
/// @brief Save an effect with EffectFileHandler
/// @param obj The effect args
/// @param[out] resultMsg The feedback message
/// @return True on success else false
/// @param obj The effect args
/// @return If not empty, it contains the error
///
bool saveEffect(const QJsonObject& obj, QString& resultMsg);
QString saveEffect(const QJsonObject& obj);
///
/// @brief Delete an effect by name.
/// @param[in] effectName The effect name to delete
/// @param[out] resultMsg The message on error
/// @return True on success else false
/// @param effectName The effect name to delete
/// @return If not empty, it contains the error
///
bool deleteEffect(const QString& effectName, QString& resultMsg);
QString deleteEffect(const QString& effectName);
///
/// @brief Get all init data of the running effects and stop them

View File

@ -29,19 +29,17 @@ public:
///
/// @brief Save an effect
/// @param obj The effect args
/// @param[out] resultMsg The feedback message
/// @return True on success else false
/// @param obj The effect args
/// @return If not empty, it contains the error
///
bool saveEffect(const QJsonObject& obj, QString& resultMsg);
QString saveEffect(const QJsonObject& obj);
///
/// @brief Delete an effect by name.
/// @param[in] effectName The effect name to delete
/// @param[out] resultMsg The message on error
/// @return True on success else false
/// @param effectName The effect name to delete
/// @return If not empty, it contains the error
///
bool deleteEffect(const QString& effectName, QString& resultMsg);
QString deleteEffect(const QString& effectName);
public slots:
///

View File

@ -20,13 +20,14 @@ class AuthManager : public QObject
private:
friend class HyperionDaemon;
/// constructor is private, can be called from HyperionDaemon
AuthManager(QObject* parent = 0);
AuthManager(QObject *parent = 0);
public:
struct AuthDefinition{
struct AuthDefinition
{
QString id;
QString comment;
QObject* caller;
QObject *caller;
uint64_t timeoutTime;
QString token;
QString lastUse;
@ -36,43 +37,25 @@ public:
/// @brief Get the unique id (imported from removed class 'Stats')
/// @return The unique id
///
const QString & getID() { return _uuid; };
///
/// @brief Get all available token entries
///
const QVector<AuthDefinition> getTokenList();
const QString &getID() { return _uuid; };
///
/// @brief Check authorization is required according to the user setting
/// @return True if authorization required else false
///
const bool & isAuthRequired() { return _authRequired; };
const bool &isAuthRequired() { return _authRequired; };
///
/// @brief Check if authorization is required for local network connections
/// @return True if authorization required else false
///
const bool & isLocalAuthRequired() { return _localAuthRequired; };
const bool &isLocalAuthRequired() { return _localAuthRequired; };
///
/// @brief Check if authorization is required for local network connections for admin access
/// @return True if authorization required else false
///
const bool & isLocalAdminAuthRequired() { return _localAdminAuthRequired; };
///
/// @brief Check if Hyperion user has default password
/// @return True if so, else false
///
const bool hasHyperionDefaultPw() { return isUserAuthorized("Hyperion","hyperion"); };
///
/// @brief Get the current valid token for user. Make sure this call is allowed!
/// @param For the defined user
/// @return The token
///
const QString getUserToken(const QString & usr = "Hyperion");
const bool &isLocalAdminAuthRequired() { return _localAdminAuthRequired; };
///
/// @brief Reset Hyperion user
@ -81,11 +64,23 @@ public:
bool resetHyperionUser();
///
/// @brief Create a new token and skip the usual chain
/// @param comment The comment that should be used for
/// @return The new Auth definition
/// @brief Check if user auth is temporary blocked due to failed attempts
/// @return True on blocked and no further Auth requests will be accepted
///
const AuthDefinition createToken(const QString& comment);
bool isUserAuthBlocked() { return (_userAuthAttempts.length() >= 10); };
///
/// @brief Check if token auth is temporary blocked due to failed attempts
/// @return True on blocked and no further Auth requests will be accepted
///
bool isTokenAuthBlocked() { return (_tokenAuthAttempts.length() >= 25); };
/// Pointer of this instance
static AuthManager *manager;
/// Get Pointer of this instance
static AuthManager *getInstance() { return manager; };
public slots:
///
/// @brief Check if user is authorized
@ -93,14 +88,14 @@ public:
/// @param pw The password
/// @return True if authorized else false
///
bool isUserAuthorized(const QString& user, const QString& pw);
bool isUserAuthorized(const QString &user, const QString &pw);
///
/// @brief Check if token is authorized
/// @param token The token
/// @return True if authorized else false
///
bool isTokenAuthorized(const QString& token);
bool isTokenAuthorized(const QString &token);
///
/// @brief Check if token is authorized
@ -108,19 +103,29 @@ public:
/// @param token The token
/// @return True if authorized else false
///
bool isUserTokenAuthorized(const QString& usr, const QString& token);
bool isUserTokenAuthorized(const QString &usr, const QString &token);
///
/// @brief Check if user auth is temporary blocked due to failed attempts
/// @return True on blocked and no further Auth requests will be accepted
/// @brief Create a new token and skip the usual chain
/// @param comment The comment that should be used for
/// @return The new Auth definition
///
bool isUserAuthBlocked(){ return (_userAuthAttempts.length() >= 10); };
AuthManager::AuthDefinition createToken(const QString &comment);
///
/// @brief Check if token auth is temporary blocked due to failed attempts
/// @return True on blocked and no further Auth requests will be accepted
/// @brief Rename a token by id
/// @param id The token id
/// @param comment The new comment
/// @return True on success else false (or not found)
///
bool isTokenAuthBlocked(){ return (_tokenAuthAttempts.length() >= 25); };
bool renameToken(const QString &id, const QString &comment);
///
/// @brief Delete a token by id
/// @param id The token id
/// @return True on success else false (or not found)
///
bool deleteToken(const QString &id);
///
/// @brief Change password of user
@ -129,7 +134,7 @@ public:
/// @param newPw The new password
/// @return True on success else false
///
bool updateUserPassword(const QString& user, const QString& pw, const QString& newPw);
bool updateUserPassword(const QString &user, const QString &pw, const QString &newPw);
///
/// @brief Generate a new pending token request with the provided comment and id as identifier helper
@ -137,55 +142,55 @@ public:
/// @param comment The comment as ident helper
/// @param id The id created by the caller
///
void setNewTokenRequest(QObject* caller, const QString& comment, const QString& id);
void setNewTokenRequest(QObject *caller, const QString &comment, const QString &id);
///
/// @brief Accept a token request by id, generate token and inform token caller
/// @param id The id of the request
/// @return True on success, false if not found
/// @brief Cancel a pending token request with the provided comment and id as identifier helper
/// @param caller The QObject of the caller to deliver the reply
/// @param comment The comment as ident helper
/// @param id The id created by the caller
///
bool acceptTokenRequest(const QString& id);
void cancelNewTokenRequest(QObject *caller, const QString &comment, const QString &id);
///
/// @brief Deny a token request by id, inform the requester
/// @brief Handle a token request by id, generate token and inform token caller or deny
/// @param id The id of the request
/// @return True on success, false if not found
/// @param accept The accept or deny the request
///
bool denyTokenRequest(const QString& id);
void handlePendingTokenRequest(const QString &id, const bool &accept);
///
/// @brief Get pending requests
/// @return All pending requests
///
const QMap<QString, AuthDefinition> getPendingRequests();
QVector<AuthManager::AuthDefinition> getPendingRequests();
///
/// @brief Delete a token by id
/// @param id The token id
/// @return True on success else false (or not found)
/// @brief Get the current valid token for user. Make sure this call is allowed!
/// @param usr the defined user
/// @return The token
///
bool deleteToken(const QString& id);
const QString getUserToken(const QString &usr = "Hyperion");
/// Pointer of this instance
static AuthManager* manager;
/// Get Pointer of this instance
static AuthManager* getInstance() { return manager; };
///
/// @brief Get all available token entries
///
QVector<AuthManager::AuthDefinition> getTokenList();
public slots:
///
/// @brief Handle settings update from Hyperion Settingsmanager emit
/// @param type settings type from enum
/// @param config configuration object
///
void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config);
void handleSettingsUpdate(const settings::type &type, const QJsonDocument &config);
signals:
///
/// @brief Emits whenever a new token Request has been created along with the id and comment
/// @param id The id of the request
/// @param comment The comment of the request
/// @param comment The comment of the request; If the comment is EMPTY, it's a revoke of the caller!
///
void newPendingTokenRequest(const QString& id, const QString& comment);
void newPendingTokenRequest(const QString &id, const QString &comment);
///
/// @brief Emits when the user has accepted or denied a token
@ -195,26 +200,32 @@ signals:
/// @param comment The comment that was part of the request
/// @param id The id that was part of the request
///
void tokenResponse(const bool& success, QObject* caller, const QString& token, const QString& comment, const QString& id);
void tokenResponse(const bool &success, QObject *caller, const QString &token, const QString &comment, const QString &id);
///
/// @brief Emits whenever the token list changes
/// @param data The full list of tokens
///
void tokenChange(QVector<AuthManager::AuthDefinition>);
private:
///
/// @brief Increment counter for token/user auth
/// @param user If true we increment USER auth instead of token
///
void setAuthBlock(const bool& user = false);
void setAuthBlock(const bool &user = false);
/// Database interface for auth table
AuthTable* _authTable;
AuthTable *_authTable;
/// Database interface for meta table
MetaTable* _metaTable;
MetaTable *_metaTable;
/// Unique ID (imported from removed class 'Stats')
QString _uuid;
/// All pending requests
QMap<QString,AuthDefinition> _pendingRequests;
QMap<QString, AuthDefinition> _pendingRequests;
/// Reflect state of global auth
bool _authRequired;
@ -226,10 +237,10 @@ private:
bool _localAdminAuthRequired;
/// Timer for counting against pendingRequest timeouts
QTimer* _timer;
QTimer *_timer;
// Timer which cleans up the block counter
QTimer* _authBlockTimer;
QTimer *_authBlockTimer;
// Contains timestamps of failed user login attempts
QVector<uint64_t> _userAuthAttempts;
@ -247,4 +258,4 @@ private slots:
/// @brief Check if there are timeouts for failed login attempts
///
void checkAuthBlockTimeout();
};
};

View File

@ -78,35 +78,8 @@ public:
///
void freeObjects(bool emitCloseSignal=false);
///
/// @brief Get a pointer to the effect engine
/// @return EffectEngine instance pointer
///
EffectEngine* getEffectEngineInstance() { return _effectEngine; };
///
/// @brief Get a pointer to the priorityMuxer instance
/// @return PriorityMuxer instance pointer
///
PriorityMuxer* getMuxerInstance() { return &_muxer; };
ImageProcessor* getImageProcessor() { return _imageProcessor; };
///
/// @brief Get a setting by settings::type from SettingsManager
/// @param type The settingsType from enum
/// @return Data Document
///
QJsonDocument getSetting(const settings::type& type);
///
/// @brief Save a complete json config
/// @param config The entire config object
/// @param correct If true will correct json against schema before save
/// @return True on success else false
///
bool saveSettings(QJsonObject config, const bool& correct = false);
///
/// @brief Get instance index of this instance
/// @return The index of this instance
@ -123,103 +96,6 @@ public:
///
QSize getLedGridSize() const { return _ledGridSize; };
///
/// Returns the current priority
///
/// @return The current priority
///
int getCurrentPriority() const;
///
/// Returns true if current priority is given priority
///
/// @return bool
///
bool isCurrentPriority(const int priority) const;
///
/// Returns a list of all registered priorities
///
/// @return The list with priorities
///
QList<int> getActivePriorities() const;
///
/// Returns the information of a specific priorrity channel
///
/// @param[in] priority The priority channel
///
/// @return The information of the given, a not found priority will return lowest priority as fallback
///
const InputInfo getPriorityInfo(const int priority) const;
///
/// @brief Save an effect
/// @param obj The effect args
/// @param[out] resultMsg The feedback message
/// @return True on success else false
///
bool saveEffect(const QJsonObject& obj, QString& resultMsg);
///
/// @brief Delete an effect by name.
/// @param[in] effectName The effect name to delete
/// @param[out] resultMsg The message on error
/// @return True on success else false
///
bool deleteEffect(const QString& effectName, QString& resultMsg);
/// Get the list of available effects
/// @return The list of available effects
const std::list<EffectDefinition> &getEffects() const;
/// Get the list of active effects
/// @return The list of active effects
const std::list<ActiveEffectDefinition> &getActiveEffects();
/// Get the list of available effect schema files
/// @return The list of available effect schema files
const std::list<EffectSchema> &getEffectSchemas();
/// gets the current json config object from SettingsManager
/// @return json config
const QJsonObject& getQJsonConfig();
/// enable/disable automatic/priorized source selection
/// @param enabled the state
void setSourceAutoSelectEnabled(bool enabled);
/// set current input source to visible
/// @param priority the priority channel which should be vidible
/// @return true if success, false on error
bool setCurrentSourcePriority(int priority );
/// gets current state of automatic/priorized source selection
/// @return the state
bool sourceAutoSelectEnabled();
///
/// @brief Called from components to update their current state. DO NOT CALL FROM USERS
/// @param[in] component The component from enum
/// @param[in] state The state of the component [true | false]
///
void setNewComponentState(const hyperion::Components& component, const bool& state);
///
/// @brief Get a list of all contrable components and their current state
/// @return list of components
///
std::map<hyperion::Components, bool> getAllComponents();
///
/// @brief Test if a component is enabled
/// @param The component to test
/// @return Component state
///
int isComponentEnabled(const hyperion::Components& comp);
ComponentRegister& getComponentRegister() { return _componentRegister; };
/// gets the methode how image is maped to leds
const int & getLedMappingType();
@ -244,6 +120,7 @@ public:
LedDevice * getActiveDevice() const;
public slots:
///
/// @brief Register a new input by priority, the priority is not active (timeout -100 isn't muxer recognized) until you start to update the data with setInput()
/// A repeated call to update the base data of a known priority won't overwrite their current timeout
@ -322,6 +199,28 @@ public slots:
///
bool clear(const int priority, bool forceClearAll=false);
/// #############
// EFFECTENGINE
///
/// @brief Get a pointer to the effect engine
/// @return EffectEngine instance pointer
///
EffectEngine* getEffectEngineInstance() { return _effectEngine; };
///
/// @brief Save an effect
/// @param obj The effect args
/// @return Empty on success else error message
///
QString saveEffect(const QJsonObject& obj);
///
/// @brief Delete an effect by name.
/// @param effectName The effect name to delete
/// @return Empty on success else error message
///
QString deleteEffect(const QString& effectName);
/// Run the specified effect on the given priority channel and optionally specify a timeout
/// @param effectName Name of the effec to run
/// @param priority The priority channel of the effect
@ -342,6 +241,122 @@ public slots:
, const QString &imageData = ""
);
/// Get the list of available effects
/// @return The list of available effects
const std::list<EffectDefinition> &getEffects() const;
/// Get the list of active effects
/// @return The list of active effects
const std::list<ActiveEffectDefinition> &getActiveEffects();
/// Get the list of available effect schema files
/// @return The list of available effect schema files
const std::list<EffectSchema> &getEffectSchemas();
/// #############
/// PRIORITYMUXER
///
/// @brief Get a pointer to the priorityMuxer instance
/// @return PriorityMuxer instance pointer
///
PriorityMuxer* getMuxerInstance() { return &_muxer; };
///
/// @brief enable/disable automatic/priorized source selection
/// @param state The new state
///
void setSourceAutoSelect(const bool state);
///
/// @brief set current input source to visible
/// @param priority the priority channel which should be vidible
/// @return true if success, false on error
///
bool setVisiblePriority(const int& priority);
/// gets current state of automatic/priorized source selection
/// @return the state
bool sourceAutoSelectEnabled();
///
/// Returns the current priority
///
/// @return The current priority
///
int getCurrentPriority() const;
///
/// Returns true if current priority is given priority
///
/// @return bool
///
bool isCurrentPriority(const int priority) const;
///
/// Returns a list of all registered priorities
///
/// @return The list with priorities
///
QList<int> getActivePriorities() const;
///
/// Returns the information of a specific priorrity channel
///
/// @param[in] priority The priority channel
///
/// @return The information of the given, a not found priority will return lowest priority as fallback
///
const InputInfo getPriorityInfo(const int priority) const;
/// #############
/// SETTINGSMANAGER
///
/// @brief Get a setting by settings::type from SettingsManager
/// @param type The settingsType from enum
/// @return Data Document
///
QJsonDocument getSetting(const settings::type& type);
/// gets the current json config object from SettingsManager
/// @return json config
const QJsonObject& getQJsonConfig();
///
/// @brief Save a complete json config
/// @param config The entire config object
/// @param correct If true will correct json against schema before save
/// @return True on success else false
///
bool saveSettings(QJsonObject config, const bool& correct = false);
/// ############
/// COMPONENTREGISTER
///
/// @brief Get the component Register
/// return Component register pointer
///
ComponentRegister& getComponentRegister() { return _componentRegister; };
///
/// @brief Called from components to update their current state. DO NOT CALL FROM USERS
/// @param[in] component The component from enum
/// @param[in] state The state of the component [true | false]
///
void setNewComponentState(const hyperion::Components& component, const bool& state);
///
/// @brief Get a list of all contrable components and their current state
/// @return list of components
///
std::map<hyperion::Components, bool> getAllComponents();
///
/// @brief Test if a component is enabled
/// @param The component to test
/// @return Component state
///
int isComponentEnabled(const hyperion::Components& comp);
/// sets the methode how image is maped to leds at ImageProcessor
void setLedMappingType(const int& mappingType);

View File

@ -32,6 +32,7 @@ public:
static HyperionIManager* getInstance() { return HIMinstance; };
static HyperionIManager* HIMinstance;
public slots:
///
/// @brief Is given instance running?
/// @param inst The instance to check

529
libsrc/api/API.cpp Normal file
View File

@ -0,0 +1,529 @@
// project includes
#include <api/API.h>
// stl includes
#include <iostream>
#include <iterator>
// Qt includes
#include <QResource>
#include <QDateTime>
#include <QCryptographicHash>
#include <QImage>
#include <QBuffer>
#include <QByteArray>
#include <QTimer>
// hyperion includes
#include <leddevice/LedDeviceWrapper.h>
#include <hyperion/GrabberWrapper.h>
#include <utils/jsonschema/QJsonFactory.h>
#include <utils/jsonschema/QJsonSchemaChecker.h>
#include <HyperionConfig.h>
#include <utils/SysInfo.h>
#include <utils/ColorSys.h>
#include <utils/Process.h>
//#include <utils/ApiSync.h>
// bonjour wrapper
#include <bonjour/bonjourbrowserwrapper.h>
// ledmapping int <> string transform methods
#include <hyperion/ImageProcessor.h>
// api includes
#include <api/JsonCB.h>
using namespace hyperion;
API::API(Logger *log, const bool &localConnection, QObject *parent)
: QObject(parent)
{
qRegisterMetaType<int64_t>("int64_t");
qRegisterMetaType<VideoMode>("VideoMode");
qRegisterMetaType<std::map<int, registerData>>("std::map<int,registerData>");
// Init
_log = log;
_authManager = AuthManager::getInstance();
_instanceManager = HyperionIManager::getInstance();
_localConnection = localConnection;
_authorized = false;
_adminAuthorized = false;
_hyperion = _instanceManager->getHyperionInstance(0);
_currInstanceIndex = 0;
// TODO FIXME
// report back current registers when a Hyperion instance request it
//connect(ApiSync::getInstance(), &ApiSync::requestActiveRegister, this, &API::requestActiveRegister, Qt::QueuedConnection);
// connect to possible token responses that has been requested
connect(_authManager, &AuthManager::tokenResponse, this, &API::checkTokenResponse);
}
void API::init(void)
{
bool apiAuthRequired = _authManager->isAuthRequired();
// For security we block external connections if default PW is set
if (!_localConnection && API::hasHyperionDefaultPw())
{
emit forceClose();
}
// if this is localConnection and network allows unauth locals, set authorized flag
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)
{
_adminAuthorized = !_authManager->isLocalAdminAuthRequired();
// just in positive direction
if (_adminAuthorized)
_authorized = true;
}
}
void API::setColor(const int &priority, const std::vector<uint8_t> &ledColors, const int &timeout_ms, const QString &origin, const hyperion::Components &callerComp)
{
std::vector<ColorRgb> fledColors;
if (ledColors.size() % 3 == 0)
{
for (unsigned i = 0; i < ledColors.size(); i += 3)
{
fledColors.emplace_back(ColorRgb{ledColors[i], ledColors[i + 1], ledColors[i + 2]});
}
QMetaObject::invokeMethod(_hyperion, "setColor", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(std::vector<ColorRgb>, fledColors), Q_ARG(int, timeout_ms), Q_ARG(QString, origin));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "setColor", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(int, priority), Q_ARG(std::vector<ColorRgb>, fledColors), Q_ARG(int, timeout_ms), Q_ARG(QString, origin), Q_ARG(hyperion::Components, callerComp));
}
}
bool API::setImage(ImageCmdData &data, hyperion::Components comp, QString &replyMsg, const hyperion::Components &callerComp)
{
// truncate name length
data.imgName.truncate(16);
if (data.format == "auto")
{
QImage img = QImage::fromData(data.data);
if (img.isNull())
{
replyMsg = "Failed to parse picture, the file might be corrupted";
return false;
}
// check for requested scale
if (data.scale > 24)
{
if (img.height() > data.scale)
{
img = img.scaledToHeight(data.scale);
}
if (img.width() > data.scale)
{
img = img.scaledToWidth(data.scale);
}
}
// check if we need to force a scale
if (img.width() > 2000 || img.height() > 2000)
{
data.scale = 2000;
if (img.height() > data.scale)
{
img = img.scaledToHeight(data.scale);
}
if (img.width() > data.scale)
{
img = img.scaledToWidth(data.scale);
}
}
data.width = img.width();
data.height = img.height();
// extract image
img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
data.data.clear();
data.data.reserve(img.width() * img.height() * 3);
for (int i = 0; i < img.height(); ++i)
{
const QRgb *scanline = reinterpret_cast<const QRgb *>(img.scanLine(i));
for (int j = 0; j < img.width(); ++j)
{
data.data.append((char)qRed(scanline[j]));
data.data.append((char)qGreen(scanline[j]));
data.data.append((char)qBlue(scanline[j]));
}
}
}
else
{
// check consistency of the size of the received data
if (data.data.size() != data.width * data.height * 3)
{
replyMsg = "Size of image data does not match with the width and height";
return false;
}
}
// copy image
Image<ColorRgb> image(data.width, data.height);
memcpy(image.memptr(), data.data.data(), data.data.size());
QMetaObject::invokeMethod(_hyperion, "registerInput", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(hyperion::Components, comp), Q_ARG(QString, data.origin), Q_ARG(QString, data.imgName));
QMetaObject::invokeMethod(_hyperion, "setInputImage", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(Image<ColorRgb>, image), Q_ARG(int64_t, data.duration));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "registerInput", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(int, data.priority), Q_ARG(hyperion::Components, comp), Q_ARG(QString, data.origin), Q_ARG(QString, data.imgName), Q_ARG(hyperion::Components, callerComp));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "setInputImage", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(int, data.priority), Q_ARG(Image<ColorRgb>, image), Q_ARG(int64_t, data.duration), Q_ARG(hyperion::Components, comp), Q_ARG(hyperion::Components, callerComp));
return true;
}
bool API::clearPriority(const int &priority, QString &replyMsg, const hyperion::Components &callerComp)
{
if (priority < 0 || (priority > 0 && priority < 254))
{
QMetaObject::invokeMethod(_hyperion, "clear", Qt::QueuedConnection, Q_ARG(int, priority));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "clearPriority", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(int, priority), Q_ARG(hyperion::Components, callerComp));
}
else
{
replyMsg = QString("Priority %1 is not allowed to be cleared").arg(priority);
return false;
}
return true;
}
bool API::setComponentState(const QString &comp, bool &compState, QString &replyMsg, const hyperion::Components &callerComp)
{
Components component = stringToComponent(comp);
if (component != COMP_INVALID)
{
QMetaObject::invokeMethod(_hyperion, "compStateChangeRequest", Qt::QueuedConnection, Q_ARG(hyperion::Components, component), Q_ARG(bool, compState));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "compStateChangeRequest", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(hyperion::Components, component), Q_ARG(bool, compState), Q_ARG(hyperion::Components, callerComp));
return true;
}
replyMsg = QString("Unknown component name: %1").arg(comp);
return false;
}
void API::setLedMappingType(const int &type, const hyperion::Components &callerComp)
{
QMetaObject::invokeMethod(_hyperion, "setLedMappingType", Qt::QueuedConnection, Q_ARG(int, type));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "setLedMappingType", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(int, type), Q_ARG(hyperion::Components, callerComp));
}
void API::setVideoMode(const VideoMode &mode, const hyperion::Components &callerComp)
{
QMetaObject::invokeMethod(_hyperion, "setVideoMode", Qt::QueuedConnection, Q_ARG(VideoMode, mode));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "setVideoMode", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(VideoMode, mode), Q_ARG(hyperion::Components, callerComp));
}
void API::setEffect(const EffectCmdData &dat, const hyperion::Components &callerComp)
{
if (!dat.args.isEmpty())
{
QMetaObject::invokeMethod(_hyperion, "setEffect", Qt::QueuedConnection, Q_ARG(QString, dat.effectName), Q_ARG(QJsonObject, dat.args), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.pythonScript), Q_ARG(QString, dat.origin), Q_ARG(QString, dat.data));
}
else
{
QMetaObject::invokeMethod(_hyperion, "setEffect", Qt::QueuedConnection, Q_ARG(QString, dat.effectName), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.origin));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "setEffect", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(QString, dat.effectName), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.origin), Q_ARG(hyperion::Components, callerComp));
}
}
void API::setSourceAutoSelect(const bool state, const hyperion::Components &callerComp)
{
QMetaObject::invokeMethod(_hyperion, "setSourceAutoSelect", Qt::QueuedConnection, Q_ARG(bool, state));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "setSourceAutoSelect", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(bool, state), Q_ARG(hyperion::Components, callerComp));
}
void API::setVisiblePriority(const int &priority, const hyperion::Components &callerComp)
{
QMetaObject::invokeMethod(_hyperion, "setVisiblePriority", Qt::QueuedConnection, Q_ARG(int, priority));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "setVisiblePriority", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(int, priority), Q_ARG(hyperion::Components, callerComp));
}
void API::registerInput(const int &priority, const hyperion::Components &component, const QString &origin, const QString &owner, const hyperion::Components &callerComp)
{
if (_activeRegisters.count(priority))
_activeRegisters.erase(priority);
_activeRegisters.insert({priority, registerData{component, origin, owner, callerComp}});
QMetaObject::invokeMethod(_hyperion, "registerInput", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(hyperion::Components, component), Q_ARG(QString, origin), Q_ARG(QString, owner));
//QMetaObject::invokeMethod(ApiSync::getInstance(), "registerInput", Qt::QueuedConnection, Q_ARG(QObject *, _hyperion), Q_ARG(int, priority), Q_ARG(hyperion::Components, component), Q_ARG(QString, origin), Q_ARG(QString, owner), Q_ARG(hyperion::Components, callerComp));
}
void API::unregisterInput(const int &priority)
{
if (_activeRegisters.count(priority))
_activeRegisters.erase(priority);
}
bool API::setHyperionInstance(const quint8 &inst)
{
if (_currInstanceIndex == inst)
return true;
bool isRunning;
QMetaObject::invokeMethod(_instanceManager, "IsInstanceRunning", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isRunning), Q_ARG(quint8, inst));
if (!isRunning)
return false;
disconnect(_hyperion, 0, this, 0);
QMetaObject::invokeMethod(_instanceManager, "getHyperionInstance", Qt::BlockingQueuedConnection, Q_RETURN_ARG(Hyperion *, _hyperion), Q_ARG(quint8, inst));
_currInstanceIndex = inst;
return true;
}
std::map<hyperion::Components, bool> API::getAllComponents()
{
std::map<hyperion::Components, bool> comps;
//QMetaObject::invokeMethod(_hyperion, "getAllComponents", Qt::BlockingQueuedConnection, Q_RETURN_ARG(std::map<hyperion::Components, bool>, comps));
return comps;
}
bool API::isHyperionEnabled()
{
int res;
QMetaObject::invokeMethod(_hyperion, "isComponentEnabled", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, res), Q_ARG(hyperion::Components, hyperion::COMP_ALL));
return res > 0;
}
QVector<QVariantMap> API::getAllInstanceData(void)
{
QVector<QVariantMap> vec;
QMetaObject::invokeMethod(_instanceManager, "getInstanceData", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVector<QVariantMap>, vec));
return vec;
}
void API::startInstance(const quint8 &index)
{
QMetaObject::invokeMethod(_instanceManager, "startInstance", Qt::QueuedConnection, Q_ARG(quint8, index));
}
void API::stopInstance(const quint8 &index)
{
QMetaObject::invokeMethod(_instanceManager, "stopInstance", Qt::QueuedConnection, Q_ARG(quint8, index));
}
void API::requestActiveRegister(QObject *callerInstance)
{
// TODO FIXME
//if (_activeRegisters.size())
// QMetaObject::invokeMethod(ApiSync::getInstance(), "answerActiveRegister", Qt::QueuedConnection, Q_ARG(QObject *, callerInstance), Q_ARG(MapRegister, _activeRegisters));
}
bool API::deleteInstance(const quint8 &index, QString &replyMsg)
{
if (_adminAuthorized)
{
QMetaObject::invokeMethod(_instanceManager, "deleteInstance", Qt::QueuedConnection, Q_ARG(quint8, index));
return true;
}
replyMsg = NO_AUTH;
return false;
}
QString API::createInstance(const QString &name)
{
if (_adminAuthorized)
{
bool success;
QMetaObject::invokeMethod(_instanceManager, "createInstance", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(QString, name));
if (!success)
return QString("Instance name '%1' is already in use").arg(name);
return "";
}
return NO_AUTH;
}
QString API::setInstanceName(const quint8 &index, const QString &name)
{
if (_adminAuthorized)
{
QMetaObject::invokeMethod(_instanceManager, "saveName", Qt::QueuedConnection, Q_ARG(quint8, index), Q_ARG(QString, name));
return "";
}
return NO_AUTH;
}
QString API::deleteEffect(const QString &name)
{
if (_adminAuthorized)
{
QString res;
QMetaObject::invokeMethod(_hyperion, "deleteEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QString, name));
return res;
}
return NO_AUTH;
}
QString API::saveEffect(const QJsonObject &data)
{
if (_adminAuthorized)
{
QString res;
QMetaObject::invokeMethod(_hyperion, "saveEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QJsonObject, data));
return res;
}
return NO_AUTH;
}
void API::saveSettings(const QJsonObject &data)
{
if (!_adminAuthorized)
return;
QMetaObject::invokeMethod(_hyperion, "saveSettings", Qt::QueuedConnection, Q_ARG(QJsonObject, data), Q_ARG(bool, true));
}
bool API::updateHyperionPassword(const QString &password, const QString &newPassword)
{
if (!_adminAuthorized)
return false;
bool res;
QMetaObject::invokeMethod(_authManager, "updateUserPassword", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, "Hyperion"), Q_ARG(QString, password), Q_ARG(QString, newPassword));
return res;
}
QString API::createToken(const QString &comment, AuthManager::AuthDefinition &def)
{
if (!_adminAuthorized)
return NO_AUTH;
if (comment.isEmpty())
return "comment is empty";
QMetaObject::invokeMethod(_authManager, "createToken", Qt::BlockingQueuedConnection, Q_RETURN_ARG(AuthManager::AuthDefinition, def), Q_ARG(QString, comment));
return "";
}
QString API::renameToken(const QString &id, const QString &comment)
{
if (!_adminAuthorized)
return NO_AUTH;
if (comment.isEmpty() || id.isEmpty())
return "Empty comment or id";
QMetaObject::invokeMethod(_authManager, "renameToken", Qt::QueuedConnection, Q_ARG(QString, id), Q_ARG(QString, comment));
return "";
}
QString API::deleteToken(const QString &id)
{
if (!_adminAuthorized)
return NO_AUTH;
if (id.isEmpty())
return "Empty id";
QMetaObject::invokeMethod(_authManager, "deleteToken", Qt::QueuedConnection, Q_ARG(QString, id));
return "";
}
void API::setNewTokenRequest(const QString &comment, const QString &id)
{
QMetaObject::invokeMethod(_authManager, "setNewTokenRequest", Qt::QueuedConnection, Q_ARG(QObject *, this), Q_ARG(QString, comment), Q_ARG(QString, id));
}
void API::cancelNewTokenRequest(const QString &comment, const QString &id)
{
QMetaObject::invokeMethod(_authManager, "cancelNewTokenRequest", Qt::QueuedConnection, Q_ARG(QObject *, this), Q_ARG(QString, comment), Q_ARG(QString, id));
}
bool API::handlePendingTokenRequest(const QString &id, const bool accept)
{
if (!_adminAuthorized)
return false;
QMetaObject::invokeMethod(_authManager, "handlePendingTokenRequest", Qt::QueuedConnection, Q_ARG(QString, id), Q_ARG(bool, accept));
return true;
}
bool API::getTokenList(QVector<AuthManager::AuthDefinition> &def)
{
if (!_adminAuthorized)
return false;
QMetaObject::invokeMethod(_authManager, "getTokenList", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVector<AuthManager::AuthDefinition>, def));
return true;
}
bool API::getPendingTokenRequests(QVector<AuthManager::AuthDefinition> &map)
{
if (!_adminAuthorized)
return false;
QMetaObject::invokeMethod(_authManager, "getPendingRequests", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVector<AuthManager::AuthDefinition>, map));
return true;
}
bool API::isUserTokenAuthorized(const QString &userToken)
{
bool res;
QMetaObject::invokeMethod(_authManager, "isUserTokenAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, "Hyperion"), Q_ARG(QString, userToken));
if (res)
{
_authorized = true;
_adminAuthorized = true;
// Listen for ADMIN ACCESS protected signals
connect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest, Qt::UniqueConnection);
}
return res;
}
bool API::getUserToken(QString &userToken)
{
if (!_adminAuthorized)
return false;
QMetaObject::invokeMethod(_authManager, "getUserToken", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, userToken));
return true;
}
bool API::isTokenAuthorized(const QString &token)
{
bool res;
QMetaObject::invokeMethod(_authManager, "isTokenAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, token));
if (res)
_authorized = true;
return res;
}
bool API::isUserAuthorized(const QString &password)
{
bool res;
QMetaObject::invokeMethod(_authManager, "isUserAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, "Hyperion"), Q_ARG(QString, password));
if (res)
{
_authorized = true;
_adminAuthorized = true;
// Listen for ADMIN ACCESS protected signals
connect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest, Qt::UniqueConnection);
}
return res;
}
bool API::hasHyperionDefaultPw()
{
bool res;
QMetaObject::invokeMethod(_authManager, "isUserAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, "Hyperion"), Q_ARG(QString, "hyperion"));
return res;
}
void API::logout()
{
_authorized = false;
_adminAuthorized = false;
// Stop listenig for ADMIN ACCESS protected signals
disconnect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest);
stopDataConnectionss();
}
void API::checkTokenResponse(const bool &success, QObject *caller, const QString &token, const QString &comment, const QString &id)
{
if (this == caller)
emit onTokenResponse(success, token, comment, id);
}
void API::stopDataConnectionss()
{
}

View File

@ -10,7 +10,7 @@
"subcommand" : {
"type" : "string",
"required" : true,
"enum" : ["requestToken","createToken","deleteToken","getTokenList","logout","login","required","adminRequired","newPasswordRequired","newPassword","answerRequest","getPendingRequests"]
"enum" : ["requestToken","createToken","renameToken","deleteToken","getTokenList","logout","login","tokenRequired","adminRequired","newPasswordRequired","newPassword","answerRequest","getPendingTokenRequests"]
},
"tan" : {
"type" : "integer"

File diff suppressed because it is too large Load Diff

View File

@ -35,7 +35,7 @@ JsonCB::JsonCB(QObject* parent)
, _prioMuxer(nullptr)
{
_availableCommands << "components-update" << "sessions-update" << "priorities-update" << "imageToLedMapping-update"
<< "adjustment-update" << "videomode-update" << "effects-update" << "settings-update" << "leds-update" << "instance-update";
<< "adjustment-update" << "videomode-update" << "effects-update" << "settings-update" << "leds-update" << "instance-update" << "token-update";
}
bool JsonCB::subscribeFor(const QString& type, const bool & unsubscribe)
@ -131,6 +131,14 @@ bool JsonCB::subscribeFor(const QString& type, const bool & unsubscribe)
connect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange, Qt::UniqueConnection);
}
if (type == "token-update")
{
if (unsubscribe)
disconnect(AuthManager::getInstance(), &AuthManager::tokenChange, this, &JsonCB::handleTokenChange);
else
connect(AuthManager::getInstance(), &AuthManager::tokenChange, this, &JsonCB::handleTokenChange, Qt::UniqueConnection);
}
return true;
}
@ -401,3 +409,17 @@ void JsonCB::handleInstanceChange()
}
doCallback("instance-update", QVariant(arr));
}
void JsonCB::handleTokenChange(const QVector<AuthManager::AuthDefinition> &def)
{
QJsonArray arr;
for (const auto &entry : def)
{
QJsonObject sub;
sub["comment"] = entry.comment;
sub["id"] = entry.id;
sub["last_use"] = entry.lastUse;
arr.push_back(sub);
}
doCallback("token-update", QVariant(arr));
}

View File

@ -47,14 +47,14 @@ EffectEngine::~EffectEngine()
{
}
bool EffectEngine::saveEffect(const QJsonObject& obj, QString& resultMsg)
QString EffectEngine::saveEffect(const QJsonObject& obj)
{
return _effectFileHandler->saveEffect(obj, resultMsg);
return _effectFileHandler->saveEffect(obj);
}
bool EffectEngine::deleteEffect(const QString& effectName, QString& resultMsg)
QString EffectEngine::deleteEffect(const QString& effectName)
{
return _effectFileHandler->deleteEffect(effectName, resultMsg);
return _effectFileHandler->deleteEffect(effectName);
}
const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()

View File

@ -58,8 +58,9 @@ void EffectFileHandler::handleSettingsUpdate(const settings::type& type, const Q
}
}
bool EffectFileHandler::deleteEffect(const QString& effectName, QString& resultMsg)
QString EffectFileHandler::deleteEffect(const QString& effectName)
{
QString resultMsg;
std::list<EffectDefinition> effectsDefinition = getEffects();
std::list<EffectDefinition>::iterator it = std::find_if(effectsDefinition.begin(), effectsDefinition.end(), find_effect(effectName));
@ -82,7 +83,7 @@ bool EffectFileHandler::deleteEffect(const QString& effectName, QString& resultM
if (result)
{
updateEffects();
return true;
return "";
} else
resultMsg = "Can't delete effect configuration file: " + effectConfigurationFile.absoluteFilePath() + ". Please check permissions";
} else
@ -92,11 +93,12 @@ bool EffectFileHandler::deleteEffect(const QString& effectName, QString& resultM
} else
resultMsg = "Effect " + effectName + " not found";
return false;
return resultMsg;
}
bool EffectFileHandler::saveEffect(const QJsonObject& message, QString& resultMsg)
QString EffectFileHandler::saveEffect(const QJsonObject& message)
{
QString resultMsg;
if (!message["args"].toObject().isEmpty())
{
QString scriptName;
@ -111,8 +113,7 @@ bool EffectFileHandler::saveEffect(const QJsonObject& message, QString& resultMs
{
if(!JsonUtils::validate("EffectFileHandler", message["args"].toObject(), it->schemaFile, _log))
{
resultMsg = "Error during arg validation against schema, please consult the Hyperion Log";
return false;
return "Error during arg validation against schema, please consult the Hyperion Log";
}
QJsonObject effectJson;
@ -123,8 +124,7 @@ bool EffectFileHandler::saveEffect(const QJsonObject& message, QString& resultMs
{
if (message["name"].toString().trimmed().isEmpty() || message["name"].toString().trimmed().startsWith("."))
{
resultMsg = "Can't save new effect. Effect name is empty or begins with a dot.";
return false;
return "Can't save new effect. Effect name is empty or begins with a dot.";
}
effectJson["name"] = message["name"].toString();
@ -140,8 +140,7 @@ bool EffectFileHandler::saveEffect(const QJsonObject& message, QString& resultMs
newFileName.setFile(iter->file);
if (newFileName.absoluteFilePath().mid(0, 1) == ":")
{
resultMsg = "The effect name '" + message["name"].toString() + "' is assigned to an internal effect. Please rename your effekt.";
return false;
return "The effect name '" + message["name"].toString() + "' is assigned to an internal effect. Please rename your effekt.";
}
} else
{
@ -156,21 +155,18 @@ bool EffectFileHandler::saveEffect(const QJsonObject& message, QString& resultMs
QFileInfo imageFileName(effectArray[0].toString().replace("$ROOT",_rootPath) + "/" + message["args"].toObject().value("image").toString());
if(!FileUtils::writeFile(imageFileName.absoluteFilePath(), QByteArray::fromBase64(message["imageData"].toString("").toUtf8()), _log))
{
resultMsg = "Error while saving image file '" + message["args"].toObject().value("image").toString() + ", please check the Hyperion Log";
return false;
return "Error while saving image file '" + message["args"].toObject().value("image").toString() + ", please check the Hyperion Log";
}
}
if(!JsonUtils::write(newFileName.absoluteFilePath(), effectJson, _log))
{
resultMsg = "Error while saving effect, please check the Hyperion Log";
return false;
return "Error while saving effect, please check the Hyperion Log";
}
Info(_log, "Reload effect list");
updateEffects();
resultMsg = "";
return true;
return "";
} else
resultMsg = "Can't save new effect. Effect path empty";
} else
@ -178,7 +174,7 @@ bool EffectFileHandler::saveEffect(const QJsonObject& message, QString& resultMs
} else
resultMsg = "Missing or empty Object 'args'";
return false;
return resultMsg;
}
void EffectFileHandler::updateEffects()

View File

@ -8,11 +8,11 @@
#include <QJsonObject>
#include <QTimer>
AuthManager* AuthManager::manager = nullptr;
AuthManager *AuthManager::manager = nullptr;
AuthManager::AuthManager(QObject* parent)
AuthManager::AuthManager(QObject *parent)
: QObject(parent)
, _authTable(new AuthTable("",this))
, _authTable(new AuthTable("", this))
, _metaTable(new MetaTable(this))
, _pendingRequests()
, _authRequired(true)
@ -21,10 +21,12 @@ AuthManager::AuthManager(QObject* parent)
{
AuthManager::manager = this;
// get uuid
_uuid = _metaTable->getUUID();
// Register meta
qRegisterMetaType<QVector<AuthManager::AuthDefinition>>("QVector<AuthManager::AuthDefinition>");
// setup timer
_timer->setInterval(1000);
connect(_timer, &QTimer::timeout, this, &AuthManager::checkTimeout);
@ -34,16 +36,16 @@ AuthManager::AuthManager(QObject* parent)
connect(_authBlockTimer, &QTimer::timeout, this, &AuthManager::checkAuthBlockTimeout);
// init with default user and password
if(!_authTable->userExist("Hyperion"))
if (!_authTable->userExist("Hyperion"))
{
_authTable->createUser("Hyperion","hyperion");
_authTable->createUser("Hyperion", "hyperion");
}
// update Hyperion user token on startup
_authTable->setUserToken("Hyperion");
}
const AuthManager::AuthDefinition AuthManager::createToken(const QString& comment)
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);
@ -55,14 +57,15 @@ const AuthManager::AuthDefinition AuthManager::createToken(const QString& commen
def.token = token;
def.id = id;
emit tokenChange(getTokenList());
return def;
}
const QVector<AuthManager::AuthDefinition> AuthManager::getTokenList()
QVector<AuthManager::AuthDefinition> AuthManager::getTokenList()
{
QVector<QVariantMap> vector = _authTable->getTokenList();
QVector<AuthManager::AuthDefinition> finalVec;
for(const auto& entry : vector)
for (const auto &entry : vector)
{
AuthDefinition def;
def.comment = entry["comment"].toString();
@ -70,67 +73,73 @@ const QVector<AuthManager::AuthDefinition> AuthManager::getTokenList()
def.lastUse = entry["last_use"].toString();
// don't add empty ids
if(!entry["id"].toString().isEmpty())
if (!entry["id"].toString().isEmpty())
finalVec.append(def);
}
return finalVec;
}
const QString AuthManager::getUserToken(const QString & usr)
const QString AuthManager::getUserToken(const QString &usr)
{
QString tok = _authTable->getUserToken(usr);
return QString(_authTable->getUserToken(usr));
}
void AuthManager::setAuthBlock(const bool& user)
void AuthManager::setAuthBlock(const bool &user)
{
// current timestamp +10 minutes
if(user)
_userAuthAttempts.append(QDateTime::currentMSecsSinceEpoch()+600000);
if (user)
_userAuthAttempts.append(QDateTime::currentMSecsSinceEpoch() + 600000);
else
_tokenAuthAttempts.append(QDateTime::currentMSecsSinceEpoch()+600000);
_tokenAuthAttempts.append(QDateTime::currentMSecsSinceEpoch() + 600000);
QMetaObject::invokeMethod(_authBlockTimer, "start", Qt::QueuedConnection);
_authBlockTimer->start();
}
bool AuthManager::isUserAuthorized(const QString& user, const QString& pw)
bool AuthManager::isUserAuthorized(const QString &user, const QString &pw)
{
if(isUserAuthBlocked())
if (isUserAuthBlocked())
return false;
if(!_authTable->isUserAuthorized(user, pw)){
if (!_authTable->isUserAuthorized(user, pw))
{
setAuthBlock(true);
return false;
}
return true;
}
bool AuthManager::isTokenAuthorized(const QString& token)
bool AuthManager::isTokenAuthorized(const QString &token)
{
if(isTokenAuthBlocked())
if (isTokenAuthBlocked())
return false;
if(!_authTable->tokenExist(token)){
if (!_authTable->tokenExist(token))
{
setAuthBlock();
return false;
}
// timestamp update
tokenChange(getTokenList());
return true;
}
bool AuthManager::isUserTokenAuthorized(const QString& usr, const QString& token)
bool AuthManager::isUserTokenAuthorized(const QString &usr, const QString &token)
{
if(isUserAuthBlocked())
if (isUserAuthBlocked())
return false;
if(!_authTable->isUserTokenAuthorized(usr, token)){
if (!_authTable->isUserTokenAuthorized(usr, token))
{
setAuthBlock(true);
return false;
}
return true;
}
bool AuthManager::updateUserPassword(const QString& user, const QString& pw, const QString& newPw)
bool AuthManager::updateUserPassword(const QString &user, const QString &pw, const QString &newPw)
{
if(isUserAuthorized(user, pw))
if (isUserAuthorized(user, pw))
return _authTable->updateUserPassword(user, newPw);
return false;
@ -141,64 +150,90 @@ bool AuthManager::resetHyperionUser()
return _authTable->resetHyperionUser();
}
void AuthManager::setNewTokenRequest(QObject* caller, const QString& comment, const QString& id)
void AuthManager::setNewTokenRequest(QObject *caller, const QString &comment, const QString &id)
{
if(!_pendingRequests.contains(id))
if (!_pendingRequests.contains(id))
{
AuthDefinition newDef {id, comment, caller, uint64_t(QDateTime::currentMSecsSinceEpoch()+60000)};
AuthDefinition newDef{id, comment, caller, uint64_t(QDateTime::currentMSecsSinceEpoch() + 180000)};
_pendingRequests[id] = newDef;
_timer->start();
emit newPendingTokenRequest(id, comment);
}
}
bool AuthManager::acceptTokenRequest(const QString& id)
void AuthManager::cancelNewTokenRequest(QObject *caller, const QString &comment, const QString &id)
{
if(_pendingRequests.contains(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;
AuthDefinition def = _pendingRequests.value(id);
if (def.caller == caller)
_pendingRequests.remove(id);
emit newPendingTokenRequest(id, "");
}
return false;
}
bool AuthManager::denyTokenRequest(const QString& id)
void AuthManager::handlePendingTokenRequest(const QString &id, const bool &accept)
{
if(_pendingRequests.contains(id))
if (_pendingRequests.contains(id))
{
AuthDefinition def = _pendingRequests.take(id);
emit tokenResponse(false, def.caller, QString(), def.comment, id);
if (accept)
{
const QString token = QUuid::createUuid().toString().remove("{").remove("}");
_authTable->createToken(token, def.comment, id);
emit tokenResponse(true, def.caller, token, def.comment, id);
emit tokenChange(getTokenList());
}
else
{
emit tokenResponse(false, def.caller, QString(), def.comment, id);
}
}
}
QVector<AuthManager::AuthDefinition> AuthManager::getPendingRequests()
{
QVector<AuthManager::AuthDefinition> finalVec;
for (const auto &entry : _pendingRequests)
{
AuthDefinition def;
def.comment = entry.comment;
def.id = entry.id;
def.timeoutTime = entry.timeoutTime - QDateTime::currentMSecsSinceEpoch();
finalVec.append(def);
}
return finalVec;
}
bool AuthManager::renameToken(const QString &id, const QString &comment)
{
if (_authTable->renameToken(id, comment))
{
emit tokenChange(getTokenList());
return true;
}
return false;
}
const QMap<QString, AuthManager::AuthDefinition> AuthManager::getPendingRequests()
bool AuthManager::deleteToken(const QString &id)
{
return _pendingRequests;
}
bool AuthManager::deleteToken(const QString& id)
{
if(_authTable->deleteToken(id))
if (_authTable->deleteToken(id))
{
//emit tokenDeleted(token);
emit tokenChange(getTokenList());
return true;
}
return false;
}
void AuthManager::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
void AuthManager::handleSettingsUpdate(const settings::type &type, const QJsonDocument &config)
{
if(type == settings::NETWORK)
if (type == settings::NETWORK)
{
const QJsonObject& obj = config.object();
const QJsonObject &obj = config.object();
_authRequired = obj["apiAuth"].toBool(true);
_localAuthRequired = obj["localApiAuth"].toBool(false);
_localAdminAuthRequired = obj["localAdminAuth"].toBool(false);
_localAdminAuthRequired = obj["localAdminAuth"].toBool(true);
}
}
@ -209,38 +244,43 @@ void AuthManager::checkTimeout()
QMapIterator<QString, AuthDefinition> i(_pendingRequests);
while (i.hasNext())
{
i.next();
i.next();
const AuthDefinition& def = i.value();
if(def.timeoutTime <= now)
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())
if (_pendingRequests.isEmpty())
_timer->stop();
}
void AuthManager::checkAuthBlockTimeout(){
void AuthManager::checkAuthBlockTimeout()
{
// handle user auth block
for (auto it = _userAuthAttempts.begin(); it != _userAuthAttempts.end(); it++) {
for (auto it = _userAuthAttempts.begin(); it != _userAuthAttempts.end(); it++)
{
// after 10 minutes, we remove the entry
if (*it < (uint64_t)QDateTime::currentMSecsSinceEpoch()) {
if (*it < (uint64_t)QDateTime::currentMSecsSinceEpoch())
{
_userAuthAttempts.erase(it--);
}
}
// handle token auth block
for (auto it = _tokenAuthAttempts.begin(); it != _tokenAuthAttempts.end(); it++) {
for (auto it = _tokenAuthAttempts.begin(); it != _tokenAuthAttempts.end(); it++)
{
// after 10 minutes, we remove the entry
if (*it < (uint64_t)QDateTime::currentMSecsSinceEpoch()) {
if (*it < (uint64_t)QDateTime::currentMSecsSinceEpoch())
{
_tokenAuthAttempts.erase(it--);
}
}
// if the lists are empty we stop
if(_userAuthAttempts.empty() && _tokenAuthAttempts.empty())
if (_userAuthAttempts.empty() && _tokenAuthAttempts.empty())
_authBlockTimer->stop();
}

View File

@ -290,13 +290,12 @@ unsigned Hyperion::getLedCount() const
return _ledString.leds().size();
}
void Hyperion::setSourceAutoSelectEnabled(bool enabled)
void Hyperion::setSourceAutoSelect(const bool state)
{
if(_muxer.setSourceAutoSelectEnabled(enabled))
update();
_muxer.setSourceAutoSelectEnabled(state);
}
bool Hyperion::setCurrentSourcePriority(int priority )
bool Hyperion::setVisiblePriority(const int& priority)
{
return _muxer.setPriority(priority);
}
@ -465,14 +464,14 @@ const Hyperion::InputInfo Hyperion::getPriorityInfo(const int priority) const
return _muxer.getInputInfo(priority);
}
bool Hyperion::saveEffect(const QJsonObject& obj, QString& resultMsg)
QString Hyperion::saveEffect(const QJsonObject& obj)
{
return _effectEngine->saveEffect(obj, resultMsg);
return _effectEngine->saveEffect(obj);
}
bool Hyperion::deleteEffect(const QString& effectName, QString& resultMsg)
QString Hyperion::deleteEffect(const QString& effectName)
{
return _effectEngine->deleteEffect(effectName, resultMsg);
return _effectEngine->deleteEffect(effectName);
}
const std::list<EffectDefinition> & Hyperion::getEffects() const