mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Feat: Add Admin API (#617)
* Push progress
TODO: rework RESET, probably to main.cpp again
* resetPassword rework
* enable administration restriction
* add short cmd for userdata
* Js apis
* Refactor JsonCB class
* Add userToken Auth
* Feat: Close connection if ext clients when def pw is set
* Feat: Protect db against pw/token tests
* WebUi PW Support (#9)
* Initial WebUi Password Support
* Small changes
* Initial WebUi Password Support
* Small changes
* Basic WebUi Token support
* added "removeStorage", added uiLock, updated login page
* Small improvments
* Small change
* Fix: prevent downgrade of authorization
* Add translation for localAdminAuth
* Feat: Show always save button in led layout
* Revert "Feat: Show always save button in led layout"
This reverts commit caad1dfcde
.
* Feat: Password change link in notification
* Fix: body padding modal overlap
* Feat: Add instance index to response on switch
* prevent schema error
Signed-off-by: Paulchen-Panther <Paulchen-Panter@protonmail.com>
* Feat: add pw save
* Feat: callout settings/pw replaced with notification
This commit is contained in:
parent
04c3bc8cc9
commit
5e559627be
35
assets/webconfig/content/login.html
Normal file
35
assets/webconfig/content/login.html
Normal file
@ -0,0 +1,35 @@
|
||||
<div class="container" style="margin:20px auto;max-width:500px;">
|
||||
<center>
|
||||
<div>
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Login</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<input name="password" class="form-control" type="password" id="password" placeholder="Password" autocomplete="off"/>
|
||||
<input name="show_pw" type="checkbox" id="show_pw"/><label for="show_pw">Show/Hide Password</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-sm btn-success" id="btn_password" onclick="requestAuthorization(document.getElementById('password').value); return false;" disabled><i class="fa fa-fw fa-unlock"></i>Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
removeOverlay();
|
||||
|
||||
$('#password').off().on('input',function(e) {
|
||||
if(e.currentTarget.value.length >= 8)
|
||||
$('#btn_password').removeAttr('disabled');
|
||||
});
|
||||
|
||||
$('#show_pw').off().on('change',function(e) {
|
||||
(e.currentTarget.checked ? $('#password').attr('type', 'text') : $('#password').attr('type', 'password'))
|
||||
});
|
||||
</script>
|
File diff suppressed because it is too large
Load Diff
@ -76,6 +76,8 @@
|
||||
"dashboard_alert_message_confsave_success" : "Deine Hyperion Konfiguration wurde erfolgreich gespeichert. Deine Änderungen sind somit übernommen.",
|
||||
"dashboard_message_global_setting_t": "Instanzunabhängige Einstellung",
|
||||
"dashboard_message_global_setting": "Die Einstellungen auf dieser Seite sind instanzunabhängig. Änderungen werden global übernommen.",
|
||||
"dashboard_message_default_password_t": "WebUi Standardpasswort gesetzt",
|
||||
"dashboard_message_default_password": "Das Standardpasswort der WebUi ist gesetzt. Wir empfehlen dringend, dieses zu ändern.",
|
||||
"dashboard_active_instance": "Ausgewählte Instanz",
|
||||
"main_menu_dashboard_token": "Dashboard",
|
||||
"main_menu_configuration_token": "Konfiguration",
|
||||
@ -312,6 +314,8 @@
|
||||
"infoDialog_effconf_created_text": "Der Effekt \"$1\" wurde erfolgreich erstellt!",
|
||||
"InfoDialog_lang_title": "Spracheinstellung",
|
||||
"InfoDialog_lang_text": "Sollte dir die Vorauswahl der automatischen Spracherkennung nicht gefallen, kannst du die Sprache hier manuell festlegen.",
|
||||
"InfoDialog_changePassword_title" : "Ändere Passwort",
|
||||
"InfoDialog_changePassword_success" : "Passwort erfolgreich gespeichert!",
|
||||
"InfoDialog_access_title": "Einstellungsstufe",
|
||||
"InfoDialog_access_text": "Je höher die Stufe je mehr Einstellungen und Funktionen stehen zur Verfügung. Empfohlen ist \"Standard\".",
|
||||
"InfoDialog_nowrite_title": "Fehler beim Schreibzugriff!",
|
||||
@ -612,6 +616,8 @@
|
||||
"edt_conf_net_apiAuth_expl":"Zwinge alle Anwendungen welche die Hyperion API nutzen sich zu authentifizieren. Aktivieren für höhere Sicherheit, da nun jede neue Anwendung einmalig von dir bestätigt werden muss.",
|
||||
"edt_conf_net_localApiAuth_title" : "Lokale API Authentifizierung",
|
||||
"edt_conf_net_localApiAuth_expl" : "Wenn aktiviert, müssen Verbindungen aus dem Heimnetzwerk mit einem Token authentifiziert werden.",
|
||||
"edt_conf_net_localAdminAuth_title":"Lokale Admin Authentifizierung",
|
||||
"edt_conf_net_localAdminAuth_expl":"Wenn aktiviert, muss der Administrationszugriff aus dem Heimnetzwerk mit einem Passwort authentifiziert werden.",
|
||||
"edt_conf_net_restirctedInternetAccessAPI_title" : "Auf IP's beschränken",
|
||||
"edt_conf_net_restirctedInternetAccessAPI_expl": "Den Zugriff auf die API durch das Internet auf bestimmte IP's beschränken",
|
||||
"edt_conf_js_heading_title": "JSON Server",
|
||||
|
@ -75,6 +75,8 @@
|
||||
"dashboard_alert_message_confsave_success" : "Your Hyperion configuration has been saved successfully. Your changes are now active.",
|
||||
"dashboard_message_global_setting_t": "Instance independent setting",
|
||||
"dashboard_message_global_setting": "The settings on this page are not depending on a specific instance. Changes will be stored globally for all instances.",
|
||||
"dashboard_message_default_password_t": "WebUi default password is set",
|
||||
"dashboard_message_default_password": "The default password for the WebUi is set. We strongly recommend to change this.",
|
||||
"dashboard_active_instance": "Selected instance",
|
||||
"main_menu_dashboard_token" : "Dashboard",
|
||||
"main_menu_configuration_token" : "Configuration",
|
||||
@ -312,6 +314,8 @@
|
||||
"InfoDialog_lang_title" : "Language setting",
|
||||
"InfoDialog_lang_text" : "If you don't like the result of the automatic language detection you could overwrite it here.",
|
||||
"InfoDialog_access_title" : "Settings level",
|
||||
"InfoDialog_changePassword_title" : "Change Password",
|
||||
"InfoDialog_changePassword_success" : "Password successfully saved!",
|
||||
"InfoDialog_access_text" : "Depending on settings level you could adjust more options or get access to more features. Recommended is the \"Default\" level.",
|
||||
"InfoDialog_nowrite_title" : "write permission error!",
|
||||
"InfoDialog_nowrite_text" : "Hyperion can't write to your current loaded configuration file. Please repair the file permissions to proceed.",
|
||||
@ -610,7 +614,9 @@
|
||||
"edt_conf_net_apiAuth_title":"API Authentication",
|
||||
"edt_conf_net_apiAuth_expl":"Enforce all applications that use the Hyperion API to authenticate themself against Hyperion (Exception see \"Local API Authentication\"). Higher security, as you control the access and revoke it at any time.",
|
||||
"edt_conf_net_localApiAuth_title" : "Local API Authentication",
|
||||
"edt_conf_net_localApiAuth_expl" : "When enabled, connections from your home network needs to authenticate themself against Hyperion too.",
|
||||
"edt_conf_net_localApiAuth_expl" : "When enabled, connections from your home network needs to authenticate themself against Hyperion with a token.",
|
||||
"edt_conf_net_localAdminAuth_title":"Local Admin API Authentication",
|
||||
"edt_conf_net_localAdminAuth_expl":"When enabled, administration access from your home network needs a password.",
|
||||
"edt_conf_net_restirctedInternetAccessAPI_title" : "Restrict to IP's",
|
||||
"edt_conf_net_restirctedInternetAccessAPI_expl": "You can restrict the access to the API through the internet to certain IP's.",
|
||||
"edt_conf_js_heading_title" : "JSON Server",
|
||||
|
@ -92,7 +92,7 @@
|
||||
</div>
|
||||
<!-- /.navbar-header -->
|
||||
|
||||
<ul class="nav navbar-top-links navbar-right">
|
||||
<ul class="nav navbar-top-links navbar-right" id="top-navbar">
|
||||
<!-- Browser built in capture stream - streamer.js -->
|
||||
<li class="dropdown" id="btn_streamer" style="display:none">
|
||||
<!-- Hidden helpers -->
|
||||
@ -170,8 +170,23 @@
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li id="btn_changePassword">
|
||||
<a>
|
||||
<div>
|
||||
<i class="fa fa-key fa-fw"></i>
|
||||
<span data-i18n="InfoDialog_changePassword_title"></span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!-- /.lock-ui -->
|
||||
<li class="dropdown" id="btn_lock_ui" style="display:none">
|
||||
<a>
|
||||
<i class="fa fa-lock fa-fw"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- /.navbar-top-left -->
|
||||
|
||||
@ -216,12 +231,6 @@
|
||||
<h4 id="dashboard_active_instance_friendly_name"></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div id="hyperion_config_write_success_notify" style="display:none;padding:0 10px;margin:0">
|
||||
<div class="bs-callout bs-callout-success">
|
||||
<h4 data-i18n="dashboard_alert_message_confsave_success_t"></h4>
|
||||
<span data-i18n="dashboard_alert_message_confsave_success"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="hyperion_global_setting_notify" style="display:none;padding:0 10px;margin:0">
|
||||
<div class="bs-callout bs-callout-warning">
|
||||
<h4 data-i18n="dashboard_message_global_setting_t"></h4>
|
||||
|
@ -72,23 +72,88 @@ $(document).ready( function() {
|
||||
|
||||
window.showOptHelp = window.serverConfig.general.showOptHelp;
|
||||
});
|
||||
|
||||
|
||||
$(window.hyperion).on("cmd-config-setconfig", function(event) {
|
||||
if (event.response.success === true) {
|
||||
$('#hyperion_config_write_success_notify').fadeIn().delay(5000).fadeOut();
|
||||
showNotification('success', $.i18n('dashboard_alert_message_confsave_success'), $.i18n('dashboard_alert_message_confsave_success_t'))
|
||||
}
|
||||
});
|
||||
|
||||
$(window.hyperion).one("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>')
|
||||
else
|
||||
//if logged on and pw != default show option to lock ui
|
||||
$("#btn_lock_ui").removeAttr('style')
|
||||
|
||||
|
||||
if (event.response.hasOwnProperty('info'))
|
||||
setStorage("loginToken", event.response.info.token, true);
|
||||
|
||||
requestServerConfigSchema();
|
||||
});
|
||||
|
||||
$(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).one("cmd-authorize-newPasswordRequired", function(event) {
|
||||
var loginToken = getStorage("loginToken", true)
|
||||
|
||||
if (event.response.info.newPasswordRequired == true)
|
||||
{
|
||||
window.defaultPasswordIsSet = true;
|
||||
|
||||
if(loginToken)
|
||||
requestTokenAuthorization(loginToken)
|
||||
else
|
||||
requestAuthorization('hyperion');
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#main-nav").attr('style', 'display:none')
|
||||
$("#top-navbar").attr('style', 'display:none')
|
||||
|
||||
if(loginToken)
|
||||
requestTokenAuthorization(loginToken)
|
||||
else
|
||||
loadContentTo("#page-content", "login")
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
$(window.hyperion).one("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)
|
||||
requestRequiresDefaultPasswortChange();
|
||||
else
|
||||
requestServerConfigSchema();
|
||||
});
|
||||
|
||||
$(window.hyperion).on("error",function(event){
|
||||
showInfoDialog("error","Error", event.reason);
|
||||
//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))
|
||||
{
|
||||
removeStorage("loginToken", true);
|
||||
requestRequiresAdminAuth();
|
||||
}
|
||||
else
|
||||
{
|
||||
showInfoDialog("error","Error", event.reason);
|
||||
}
|
||||
});
|
||||
|
||||
$(window.hyperion).on("open",function(event){
|
||||
requestAuthorization();
|
||||
requestRequiresAdminAuth();
|
||||
});
|
||||
|
||||
$(window.hyperion).one("ready", function(event) {
|
||||
@ -190,3 +255,8 @@ $(function(){
|
||||
$(this).toggleClass('active inactive');
|
||||
});
|
||||
});
|
||||
|
||||
// hotfix body padding when bs modals overlap
|
||||
$(document.body).on('hide.bs.modal,hidden.bs.modal', function () {
|
||||
$('body').css('padding-right','0');
|
||||
});
|
||||
|
@ -28,6 +28,7 @@ window.wSess = [];
|
||||
window.currentHyperionInstance = 0;
|
||||
window.currentHyperionInstanceName = "?";
|
||||
window.comps = [];
|
||||
window.defaultPasswordIsSet = null;
|
||||
tokenList = {};
|
||||
|
||||
function initRestart()
|
||||
@ -165,9 +166,30 @@ function sendToHyperion(command, subcommand, msg)
|
||||
// -----------------------------------------------------------
|
||||
// wrapped server commands
|
||||
|
||||
function requestAuthorization()
|
||||
// Test if admin requires authentication
|
||||
function requestRequiresAdminAuth()
|
||||
{
|
||||
sendToHyperion("authorize","login",'"username": "Hyperion", "password": "hyperion"');
|
||||
sendToHyperion("authorize","adminRequired");
|
||||
}
|
||||
// Test if the default password needs to be changed
|
||||
function requestRequiresDefaultPasswortChange()
|
||||
{
|
||||
sendToHyperion("authorize","newPasswordRequired");
|
||||
}
|
||||
// Change password
|
||||
function requestChangePassword(oldPw, newPw)
|
||||
{
|
||||
sendToHyperion("authorize","newPassword",'"password": "'+oldPw+'", "newPassword":"'+newPw+'"');
|
||||
}
|
||||
|
||||
function requestAuthorization(password)
|
||||
{
|
||||
sendToHyperion("authorize","login",'"password": "' + password + '"');
|
||||
}
|
||||
|
||||
function requestTokenAuthorization(token)
|
||||
{
|
||||
sendToHyperion("authorize","login",'"token": "' + token + '"');
|
||||
}
|
||||
|
||||
function requestToken(comment)
|
||||
|
@ -4,6 +4,26 @@ var availLang = ['en','de','es','it','cs'];
|
||||
var availAccess = ['default','advanced','expert'];
|
||||
//$.i18n.debug = true;
|
||||
|
||||
//Change Password
|
||||
function changePassword(){
|
||||
showInfoDialog('changePassword', $.i18n('InfoDialog_changePassword_title'));
|
||||
|
||||
// fill default pw if default is set
|
||||
if(window.defaultPasswordIsSet)
|
||||
$('#oldPw').val('hyperion')
|
||||
|
||||
$('#id_btn_ok').off().on('click',function() {
|
||||
var oldPw = $('#oldPw').val();
|
||||
var newPw = $('#newPw').val();
|
||||
|
||||
requestChangePassword(oldPw, newPw)
|
||||
});
|
||||
|
||||
$('#newPw, #oldPw').off().on('input',function(e) {
|
||||
($('#oldPw').val().length >= 8 && $('#newPw').val().length >= 8) ? $('#id_btn_ok').attr('disabled', false) : $('#id_btn_ok').attr('disabled', true);
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready( function() {
|
||||
|
||||
//i18n
|
||||
@ -112,6 +132,17 @@ $(document).ready( function() {
|
||||
$('#id_select').trigger('change');
|
||||
});
|
||||
|
||||
// change pw btn
|
||||
$('#btn_changePassword').off().on('click',function() {
|
||||
changePassword();
|
||||
});
|
||||
|
||||
//Lock Ui
|
||||
$('#btn_lock_ui').off().on('click',function() {
|
||||
removeStorage('loginToken', true);
|
||||
location.replace('/');
|
||||
});
|
||||
|
||||
//hide menu elements
|
||||
if (storedAccess != 'expert')
|
||||
$('#load_webconfig').toggle(false);
|
||||
|
@ -297,6 +297,15 @@ function showInfoDialog(type,header,message)
|
||||
$('#id_footer_rename').html('<button type="button" id="id_btn_ok" class="btn btn-success" data-dismiss-modal="#modal_dialog_rename" disabled><i class="fa fa-fw fa-save"></i>'+$.i18n('general_btn_ok')+'</button>');
|
||||
$('#id_footer_rename').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
|
||||
}
|
||||
else if (type == "changePassword")
|
||||
{
|
||||
$('#id_body_rename').html('<i style="margin-bottom:20px" class="fa fa-key modal-icon-edit"><br>');
|
||||
$('#id_body_rename').append('<h4>'+header+'</h4>');
|
||||
$('#id_body_rename').append('<input class="form-control" id="oldPw" placeholder="Old" type="text"> <br />');
|
||||
$('#id_body_rename').append('<input class="form-control" id="newPw" placeholder="New" type="text">');
|
||||
$('#id_footer_rename').html('<button type="button" id="id_btn_ok" class="btn btn-success" data-dismiss-modal="#modal_dialog_rename" disabled><i class="fa fa-fw fa-save"></i>'+$.i18n('general_btn_ok')+'</button>');
|
||||
$('#id_footer_rename').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
|
||||
}
|
||||
else if (type == "checklist")
|
||||
{
|
||||
$('#id_body').html('<img style="margin-bottom:20px" src="img/hyperion/hyperionlogo.png" alt="Redefine ambient light!">');
|
||||
@ -326,7 +335,7 @@ function showInfoDialog(type,header,message)
|
||||
$('#id_body').append('<select id="id_select" class="form-control" style="margin-top:10px;width:auto;"></select>');
|
||||
|
||||
|
||||
$(type == "renInst" ? "#modal_dialog_rename" : "#modal_dialog").modal({
|
||||
$(type == "renInst" || type == "changePassword" ? "#modal_dialog_rename" : "#modal_dialog").modal({
|
||||
backdrop : "static",
|
||||
keyboard: false,
|
||||
show: true
|
||||
@ -534,8 +543,9 @@ function hexToRgb(hex) {
|
||||
@param type Valid types are "info","success","warning","danger"
|
||||
@param message The message to show
|
||||
@param title A title (optional)
|
||||
@param addhtml Add custom html to the notification end
|
||||
*/
|
||||
function showNotification(type, message, title="")
|
||||
function showNotification(type, message, title="", addhtml="")
|
||||
{
|
||||
if(title == "")
|
||||
{
|
||||
@ -564,15 +574,19 @@ function showNotification(type, message, title="")
|
||||
// settings
|
||||
type: type,
|
||||
animate: {
|
||||
enter: 'animated fadeInRight',
|
||||
exit: 'animated fadeOutRight'
|
||||
enter: 'animated fadeInDown',
|
||||
exit: 'animated fadeOutUp'
|
||||
},
|
||||
placement:{
|
||||
align:'center'
|
||||
},
|
||||
mouse_over : 'pause',
|
||||
template: '<div data-notify="container" class="bg-w col-xs-11 col-sm-3 bs-callout bs-callout-{0}" role="alert">' +
|
||||
template: '<div data-notify="container" class="bg-w col-md-6 bs-callout bs-callout-{0}" role="alert">' +
|
||||
'<button type="button" aria-hidden="true" class="close" data-notify="dismiss">×</button>' +
|
||||
'<span data-notify="icon"></span> ' +
|
||||
'<h4 data-notify="title">{1}</h4> ' +
|
||||
'<span data-notify="message">{2}</span>' +
|
||||
addhtml+
|
||||
'<div class="progress" data-notify="progressbar">' +
|
||||
'<div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div>' +
|
||||
'</div>' +
|
||||
|
@ -306,13 +306,21 @@
|
||||
"v4lPriority" : 240
|
||||
},
|
||||
|
||||
/// The configuration of the network security restrictions, contains the following items:
|
||||
/// * internetAccessAPI : When true allows connection from internet to the API. When false it blocks all outside connections
|
||||
/// * restirctedInternetAccessAPI : webui voodoo only - ignore it
|
||||
/// * ipWhitelist : Whitelist ip addresses from the internet to allow access to the API
|
||||
/// * apiAuth : When true the API requires authentication through tokens to use the API. Read also "localApiAuth"
|
||||
/// * localApiAuth : When false connections from the local network don't require an API authentification.
|
||||
/// * localAdminApiAuth : When false connections from the local network don't require an authentification for administration access.
|
||||
"network" :
|
||||
{
|
||||
"internetAccessAPI" : false,
|
||||
"restirctedInternetAccessAPI" : false,
|
||||
"ipWhitelist" : [],
|
||||
"apiAuth" : true,
|
||||
"localApiAuth" : false
|
||||
"localApiAuth" : false,
|
||||
"localAdminAuth": true
|
||||
},
|
||||
|
||||
/// Recreate and save led layouts made with web config. These values are just helpers for ui, not for Hyperion.
|
||||
|
@ -181,7 +181,8 @@
|
||||
"restirctedInternetAccessAPI" : false,
|
||||
"ipWhitelist" : [],
|
||||
"apiAuth" : true,
|
||||
"localApiAuth" : false
|
||||
"localApiAuth" : false,
|
||||
"localAdminAuth": true
|
||||
},
|
||||
|
||||
"ledConfig" :
|
||||
|
@ -37,6 +37,11 @@ public:
|
||||
///
|
||||
void handleMessage(const QString & message, const QString& httpAuthHeader = "");
|
||||
|
||||
///
|
||||
/// @brief Initialization steps
|
||||
///
|
||||
void initialize(void);
|
||||
|
||||
public slots:
|
||||
///
|
||||
/// @brief Is called whenever the current Hyperion instance pushes new led raw values (if enabled)
|
||||
@ -92,6 +97,11 @@ 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;
|
||||
@ -112,6 +122,9 @@ private:
|
||||
/// Log instance
|
||||
Logger* _log;
|
||||
|
||||
/// Is this a local connection
|
||||
bool _localConnection;
|
||||
|
||||
/// Hyperion instance manager
|
||||
HyperionIManager* _instanceManager;
|
||||
|
||||
@ -323,4 +336,9 @@ private:
|
||||
/// @param error String describing the error
|
||||
///
|
||||
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);
|
||||
};
|
||||
|
@ -23,25 +23,38 @@ class JsonCB : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
JsonCB(Hyperion* hyperion, QObject* parent);
|
||||
JsonCB(QObject* parent);
|
||||
|
||||
///
|
||||
/// @brief Subscribe to future data updates given by cmd
|
||||
/// @param cmd The cmd which will be subscribed for
|
||||
/// @param cmd The cmd which will be subscribed for
|
||||
/// @param unsubscribe Revert subscription
|
||||
/// @return True on success, false if not found
|
||||
///
|
||||
bool subscribeFor(const QString& cmd);
|
||||
bool subscribeFor(const QString& cmd, const bool & unsubscribe = false);
|
||||
|
||||
///
|
||||
/// @brief Get all possible commands to subscribe for
|
||||
/// @return The list of commands
|
||||
///
|
||||
QStringList getCommands() { return _availableCommands; };
|
||||
|
||||
///
|
||||
/// @brief Get all subscribed commands
|
||||
/// @return The list of commands
|
||||
///
|
||||
QStringList getSubscribedCommands() { return _subscribedCommands; };
|
||||
|
||||
///
|
||||
/// @brief Reset subscriptions, disconnect all signals
|
||||
///
|
||||
void resetSubscriptions(void);
|
||||
|
||||
///
|
||||
/// @brief Re-apply all current subs to a new Hyperion instance, the connections to the old instance will be dropped
|
||||
///
|
||||
void setSubscriptionsTo(Hyperion* hyperion);
|
||||
|
||||
signals:
|
||||
///
|
||||
/// @brief Emits whenever a new json mesage callback is ready to send
|
||||
|
@ -16,9 +16,14 @@ class AuthTable : public DBManager
|
||||
|
||||
public:
|
||||
/// construct wrapper with auth table
|
||||
AuthTable(QObject* parent = nullptr)
|
||||
AuthTable(const QString& rootPath = "", QObject* parent = nullptr)
|
||||
: DBManager(parent)
|
||||
{
|
||||
if(!rootPath.isEmpty()){
|
||||
// Init Hyperion database usage
|
||||
setRootPath(rootPath);
|
||||
setDatabaseName("hyperion");
|
||||
}
|
||||
// init Auth table
|
||||
setTable("auth");
|
||||
// create table columns
|
||||
@ -75,6 +80,82 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if a user token is authorized for access.
|
||||
/// @param usr The user name
|
||||
/// @param token The token
|
||||
/// @return True on success else false
|
||||
///
|
||||
inline bool isUserTokenAuthorized(const QString& usr, const QString& token)
|
||||
{
|
||||
if(getUserToken(usr) == token.toUtf8())
|
||||
{
|
||||
updateUserUsed(usr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Update token of a user. It's an alternate login path which is replaced on startup. This token is NOT hashed(!)
|
||||
/// @param user The user name
|
||||
/// @return True on success else false
|
||||
///
|
||||
inline bool setUserToken(const QString& user)
|
||||
{
|
||||
QVariantMap map;
|
||||
map["token"] = QCryptographicHash::hash(QUuid::createUuid().toByteArray(), QCryptographicHash::Sha512).toHex();
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user", user));
|
||||
return updateRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get token of a user. This token is NOT hashed(!)
|
||||
/// @param user The user name
|
||||
/// @return The token
|
||||
///
|
||||
inline const QByteArray getUserToken(const QString& user)
|
||||
{
|
||||
QVariantMap results;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user", user));
|
||||
getRecord(cond, results, QStringList()<<"token");
|
||||
|
||||
return results["token"].toByteArray();
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief update password of given user. The user should be tested (isUserAuthorized) to verify this change
|
||||
/// @param user The user name
|
||||
/// @param newPw The new password to set
|
||||
/// @return True on success else false
|
||||
///
|
||||
inline bool updateUserPassword(const QString& user, const QString& newPw)
|
||||
{
|
||||
QVariantMap map;
|
||||
map["password"] = calcPasswordHashOfUser(user, newPw);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user", user));
|
||||
return updateRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Reset password of Hyperion user !DANGER! Used in Hyperion main.cpp
|
||||
/// @return True on success else false
|
||||
///
|
||||
inline bool resetHyperionUser()
|
||||
{
|
||||
QVariantMap map;
|
||||
map["password"] = calcPasswordHashOfUser("Hyperion", "hyperion");
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user", "Hyperion"));
|
||||
return updateRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Update 'last_use' column entry for the corresponding user
|
||||
/// @param[in] user The user to search for
|
||||
|
@ -47,13 +47,38 @@ public:
|
||||
/// @brief Check authorization is required according to the user setting
|
||||
/// @return True if authorization required else false
|
||||
///
|
||||
bool & isAuthRequired();
|
||||
const bool & isAuthRequired() { return _authRequired; };
|
||||
|
||||
///
|
||||
/// @brief Check if authorization is required for local network connections
|
||||
/// @return True if authorization required else false
|
||||
///
|
||||
bool & isLocalAuthRequired();
|
||||
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");
|
||||
|
||||
///
|
||||
/// @brief Reset Hyperion user
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool resetHyperionUser();
|
||||
|
||||
///
|
||||
/// @brief Create a new token and skip the usual chain
|
||||
@ -77,6 +102,35 @@ public:
|
||||
///
|
||||
bool isTokenAuthorized(const QString& token);
|
||||
|
||||
///
|
||||
/// @brief Check if token is authorized
|
||||
/// @param usr The username
|
||||
/// @param token The token
|
||||
/// @return True if authorized else false
|
||||
///
|
||||
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
|
||||
///
|
||||
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); };
|
||||
|
||||
///
|
||||
/// @brief Change password of user
|
||||
/// @param user The username
|
||||
/// @param pw The CURRENT password
|
||||
/// @param newPw The new password
|
||||
/// @return True on success else false
|
||||
///
|
||||
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
|
||||
/// @param caller The QObject of the caller to deliver the reply
|
||||
@ -144,6 +198,12 @@ signals:
|
||||
void tokenResponse(const bool& success, QObject* caller, const QString& token, const QString& comment, const QString& id);
|
||||
|
||||
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);
|
||||
|
||||
/// Database interface for auth table
|
||||
AuthTable* _authTable;
|
||||
|
||||
@ -162,12 +222,29 @@ private:
|
||||
/// Reflect state of local auth
|
||||
bool _localAuthRequired;
|
||||
|
||||
/// Reflect state of local admin auth
|
||||
bool _localAdminAuthRequired;
|
||||
|
||||
/// Timer for counting against pendingRequest timeouts
|
||||
QTimer* _timer;
|
||||
|
||||
// Timer which cleans up the block counter
|
||||
QTimer* _authBlockTimer;
|
||||
|
||||
// Contains timestamps of failed user login attempts
|
||||
QVector<uint64_t> _userAuthAttempts;
|
||||
|
||||
// Contains timestamps of failed token login attempts
|
||||
QVector<uint64_t> _tokenAuthAttempts;
|
||||
|
||||
private slots:
|
||||
///
|
||||
/// @brief Check timeout of pending requests
|
||||
///
|
||||
void checkTimeout();
|
||||
|
||||
///
|
||||
/// @brief Check if there are timeouts for failed login attempts
|
||||
///
|
||||
void checkAuthBlockTimeout();
|
||||
};
|
||||
|
@ -10,19 +10,19 @@
|
||||
"subcommand" : {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"enum" : ["requestToken","createToken","deleteToken","getTokenList","logout","login","required","answerRequest","getPendingRequests"]
|
||||
"enum" : ["requestToken","createToken","deleteToken","getTokenList","logout","login","required","adminRequired","newPasswordRequired","newPassword","answerRequest","getPendingRequests"]
|
||||
},
|
||||
"tan" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"minLength" : 3
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength" : 8
|
||||
},
|
||||
"newPassword": {
|
||||
"type": "string",
|
||||
"minLength" : 8
|
||||
},
|
||||
"token": {
|
||||
"type": "string",
|
||||
"minLength" : 36
|
||||
|
@ -48,19 +48,37 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, const bool& localConnection,
|
||||
, _noListener(noListener)
|
||||
, _peerAddress(peerAddress)
|
||||
, _log(log)
|
||||
, _localConnection(localConnection)
|
||||
, _instanceManager(HyperionIManager::getInstance())
|
||||
, _hyperion(nullptr)
|
||||
, _jsonCB(nullptr)
|
||||
, _jsonCB(new JsonCB(this))
|
||||
, _streaming_logging_activated(false)
|
||||
, _imageStreamTimer(new QTimer(this))
|
||||
, _ledStreamTimer(new QTimer(this))
|
||||
{
|
||||
Q_INIT_RESOURCE(JSONRPC_schemas);
|
||||
}
|
||||
|
||||
void JsonAPI::initialize(void)
|
||||
{
|
||||
// For security we block external connections if default PW is set
|
||||
if(!_localConnection && _authManager->hasHyperionDefaultPw())
|
||||
{
|
||||
emit forceClose();
|
||||
}
|
||||
// if this is localConnection and network allows unauth locals, set authorized flag
|
||||
if(_apiAuthRequired && localConnection)
|
||||
if(_apiAuthRequired && _localConnection)
|
||||
_authorized = !_authManager->isLocalAuthRequired();
|
||||
|
||||
// admin access is allowed, when the connection is local and the option for local admin isn't set. Con: All local connections get full access
|
||||
if(_localConnection)
|
||||
{
|
||||
_userAuthorized = !_authManager->isLocalAdminAuthRequired();
|
||||
// just in positive direction
|
||||
if(_userAuthorized)
|
||||
_authorized = true;
|
||||
}
|
||||
|
||||
// setup auth interface
|
||||
connect(_authManager, &AuthManager::newPendingTokenRequest, this, &JsonAPI::handlePendingTokenRequest);
|
||||
connect(_authManager, &AuthManager::tokenResponse, this, &JsonAPI::handleTokenResponse);
|
||||
@ -68,6 +86,9 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, const bool& localConnection,
|
||||
// listen for killed instances
|
||||
connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &JsonAPI::handleInstanceStateChange);
|
||||
|
||||
// pipe callbacks from subscriptions to parent
|
||||
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
|
||||
|
||||
// init Hyperion pointer
|
||||
handleInstanceSwitch(0);
|
||||
|
||||
@ -91,30 +112,8 @@ bool JsonAPI::handleInstanceSwitch(const quint8& inst, const bool& forced)
|
||||
// get new Hyperion pointer
|
||||
_hyperion = _instanceManager->getHyperionInstance(inst);
|
||||
|
||||
// the JsonCB creates json messages you can subscribe to e.g. data change events; forward them to the parent client
|
||||
QStringList cbCmds;
|
||||
if(_jsonCB != nullptr)
|
||||
{
|
||||
cbCmds = _jsonCB->getSubscribedCommands();
|
||||
delete _jsonCB;
|
||||
}
|
||||
|
||||
_jsonCB = new JsonCB(_hyperion, this);
|
||||
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
|
||||
|
||||
// read subs
|
||||
for(const auto & entry : cbCmds)
|
||||
{
|
||||
_jsonCB->subscribeFor(entry);
|
||||
}
|
||||
|
||||
// // imageStream last state
|
||||
// if(_ledcolorsImageActive)
|
||||
// connect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage, Qt::UniqueConnection);
|
||||
//
|
||||
// //ledColor stream last state
|
||||
// if(_ledcolorsLedsActive)
|
||||
// connect(_hyperion, &Hyperion::rawLedColors, this, &JsonAPI::streamLedcolorsUpdate, Qt::UniqueConnection);
|
||||
// the JsonCB creates json messages you can subscribe to e.g. data change events
|
||||
_jsonCB->setSubscriptionsTo(_hyperion);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -727,7 +726,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
|
||||
for(const auto & entry : subsArr)
|
||||
{
|
||||
// config callbacks just if auth is set
|
||||
if(entry == "settings-update" && !_authorized)
|
||||
if(entry == "settings-update" && !_userAuthorized)
|
||||
continue;
|
||||
|
||||
if(!_jsonCB->subscribeFor(entry.toString()))
|
||||
@ -887,17 +886,28 @@ void JsonAPI::handleConfigCommand(const QJsonObject& message, const QString& com
|
||||
}
|
||||
else if (subcommand == "setconfig")
|
||||
{
|
||||
handleConfigSetCommand(message, full_command, tan);
|
||||
if(_userAuthorized)
|
||||
handleConfigSetCommand(message, full_command, tan);
|
||||
else
|
||||
sendErrorReply("No Authorization",command, tan);
|
||||
}
|
||||
else if (subcommand == "getconfig")
|
||||
{
|
||||
sendSuccessDataReply(QJsonDocument(_hyperion->getQJsonConfig()), full_command, tan);
|
||||
if(_userAuthorized)
|
||||
sendSuccessDataReply(QJsonDocument(_hyperion->getQJsonConfig()), full_command, tan);
|
||||
else
|
||||
sendErrorReply("No Authorization",command, tan);
|
||||
}
|
||||
else if (subcommand == "reload")
|
||||
{
|
||||
_hyperion->freeObjects(true);
|
||||
Process::restartHyperion();
|
||||
sendErrorReply("failed to restart hyperion", full_command, tan);
|
||||
if(_userAuthorized)
|
||||
{
|
||||
_hyperion->freeObjects(true);
|
||||
Process::restartHyperion();
|
||||
sendErrorReply("failed to restart hyperion", full_command, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization",command, tan);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1139,11 +1149,33 @@ void JsonAPI::handleVideoModeCommand(const QJsonObject& message, const QString &
|
||||
void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString &command, const int tan)
|
||||
{
|
||||
const QString& subc = message["subcommand"].toString().trimmed();
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
const QString& password = message["password"].toString().trimmed();
|
||||
const QString& newPassword = message["newPassword"].toString().trimmed();
|
||||
|
||||
// catch test if auth is required
|
||||
if(subc == "required")
|
||||
{
|
||||
QJsonObject req;
|
||||
req["required"] = _apiAuthRequired;
|
||||
req["required"] = !_authorized;
|
||||
sendSuccessDataReply(QJsonDocument(req), command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// catch test if admin auth is required
|
||||
if(subc == "adminRequired")
|
||||
{
|
||||
QJsonObject req;
|
||||
req["adminRequired"] = !_userAuthorized;
|
||||
sendSuccessDataReply(QJsonDocument(req), command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// default hyperion password is a security risk, replace it asap
|
||||
if(subc == "newPasswordRequired")
|
||||
{
|
||||
QJsonObject req;
|
||||
req["newPasswordRequired"] = _authManager->hasHyperionDefaultPw();
|
||||
sendSuccessDataReply(QJsonDocument(req), command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
@ -1153,10 +1185,30 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
{
|
||||
_authorized = false;
|
||||
_userAuthorized = false;
|
||||
// disconnect all kind of data callbacks
|
||||
stopDataConnections();
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// change password
|
||||
if(subc == "newPassword")
|
||||
{
|
||||
// use password, newPassword
|
||||
if(_userAuthorized)
|
||||
{
|
||||
if(_authManager->updateUserPassword("Hyperion", password, newPassword))
|
||||
{
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
sendErrorReply("Failed to update user password",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
sendErrorReply("No Authorization",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// token created from ui
|
||||
if(subc == "createToken")
|
||||
{
|
||||
@ -1180,11 +1232,11 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
// delete token
|
||||
if(subc == "deleteToken")
|
||||
{
|
||||
const QString& did = message["id"].toString().trimmed();
|
||||
// use id
|
||||
// for user authorized sessions
|
||||
if(_userAuthorized)
|
||||
{
|
||||
_authManager->deleteToken(did);
|
||||
_authManager->deleteToken(id);
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
@ -1195,8 +1247,8 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
// catch token request
|
||||
if(subc == "requestToken")
|
||||
{
|
||||
// use id
|
||||
const QString& comment = message["comment"].toString().trimmed();
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
_authManager->setNewTokenRequest(this, comment, id);
|
||||
// client should wait for answer
|
||||
return;
|
||||
@ -1228,7 +1280,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
// accept/deny token request
|
||||
if(subc == "answerRequest")
|
||||
{
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
// use id
|
||||
const bool& accept = message["accept"].toBool(false);
|
||||
if(_userAuthorized)
|
||||
{
|
||||
@ -1245,7 +1297,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
// deny token request
|
||||
if(subc == "acceptRequest")
|
||||
{
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
// use id
|
||||
if(_userAuthorized)
|
||||
{
|
||||
_authManager->acceptTokenRequest(id);
|
||||
@ -1256,7 +1308,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
return;
|
||||
}
|
||||
|
||||
// cath get token list
|
||||
// catch get token list
|
||||
if(subc == "getTokenList")
|
||||
{
|
||||
if(_userAuthorized)
|
||||
@ -1283,12 +1335,27 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
// login
|
||||
if(subc == "login")
|
||||
{
|
||||
// catch token auth
|
||||
const QString& token = message["token"].toString().trimmed();
|
||||
|
||||
// catch token
|
||||
if(!token.isEmpty())
|
||||
{
|
||||
if(token.count() >= 36)
|
||||
// userToken is longer
|
||||
if(token.count() > 36)
|
||||
{
|
||||
if(_authManager->isUserTokenAuthorized("Hyperion",token))
|
||||
{
|
||||
_authorized = true;
|
||||
_userAuthorized = true;
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization", command+"-"+subc, tan);
|
||||
|
||||
return;
|
||||
}
|
||||
// usual app token is 36
|
||||
if(token.count() == 36)
|
||||
{
|
||||
if(_authManager->isTokenAuthorized(token))
|
||||
{
|
||||
@ -1304,23 +1371,25 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString
|
||||
return;
|
||||
}
|
||||
|
||||
// user & password
|
||||
const QString& user = message["username"].toString().trimmed();
|
||||
const QString& password = message["password"].toString().trimmed();
|
||||
// password
|
||||
// use password
|
||||
|
||||
if(user.count() >= 3 && password.count() >= 8)
|
||||
if(password.count() >= 8)
|
||||
{
|
||||
if(_authManager->isUserAuthorized(user, password))
|
||||
if(_authManager->isUserAuthorized("Hyperion", password))
|
||||
{
|
||||
_authorized = true;
|
||||
_userAuthorized = true;
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
// Return the current valid Hyperion user token
|
||||
QJsonObject obj;
|
||||
obj["token"] = _authManager->getUserToken();
|
||||
sendSuccessDataReply(QJsonDocument(obj),command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization", command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("User or password string too short", command+"-"+subc, tan);
|
||||
sendErrorReply("Password string too short", command+"-"+subc, tan);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1343,8 +1412,11 @@ void JsonAPI::handleInstanceCommand(const QJsonObject & message, const QString &
|
||||
|
||||
if(subc == "switchTo")
|
||||
{
|
||||
if(handleInstanceSwitch(inst))
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
if(handleInstanceSwitch(inst)){
|
||||
QJsonObject obj;
|
||||
obj["instance"] = inst;
|
||||
sendSuccessDataReply(QJsonDocument(obj),command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("Selected Hyperion instance isn't running",command+"-"+subc, tan);
|
||||
return;
|
||||
@ -1576,3 +1648,13 @@ void JsonAPI::handleInstanceStateChange(const instanceState& state, const quint8
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonAPI::stopDataConnections(void)
|
||||
{
|
||||
LoggerManager::getInstance()->disconnect();
|
||||
_streaming_logging_activated = false;
|
||||
_jsonCB->resetSubscriptions();
|
||||
_imageStreamTimer->stop();
|
||||
_ledStreamTimer->stop();
|
||||
|
||||
}
|
||||
|
@ -27,87 +27,138 @@
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
JsonCB::JsonCB(Hyperion* hyperion, QObject* parent)
|
||||
JsonCB::JsonCB(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _hyperion(hyperion)
|
||||
, _componentRegister(& _hyperion->getComponentRegister())
|
||||
, _hyperion(nullptr)
|
||||
, _componentRegister(nullptr)
|
||||
, _bonjour(BonjourBrowserWrapper::getInstance())
|
||||
, _prioMuxer(_hyperion->getMuxerInstance())
|
||||
, _prioMuxer(nullptr)
|
||||
{
|
||||
_availableCommands << "components-update" << "sessions-update" << "priorities-update" << "imageToLedMapping-update"
|
||||
<< "adjustment-update" << "videomode-update" << "effects-update" << "settings-update" << "leds-update" << "instance-update";
|
||||
}
|
||||
|
||||
bool JsonCB::subscribeFor(const QString& type)
|
||||
bool JsonCB::subscribeFor(const QString& type, const bool & unsubscribe)
|
||||
{
|
||||
if(!_availableCommands.contains(type))
|
||||
return false;
|
||||
|
||||
if(unsubscribe)
|
||||
_subscribedCommands.removeAll(type);
|
||||
else
|
||||
_subscribedCommands << type;
|
||||
|
||||
if(type == "components-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState);
|
||||
else
|
||||
connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "sessions-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_bonjour, &BonjourBrowserWrapper::browserChange, this, &JsonCB::handleBonjourChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_bonjour, &BonjourBrowserWrapper::browserChange, this, &JsonCB::handleBonjourChange);
|
||||
else
|
||||
connect(_bonjour, &BonjourBrowserWrapper::browserChange, this, &JsonCB::handleBonjourChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "priorities-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection);
|
||||
connect(_prioMuxer, &PriorityMuxer::autoSelectChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection);
|
||||
if(unsubscribe){
|
||||
disconnect(_prioMuxer,0 ,0 ,0);
|
||||
} else {
|
||||
connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection);
|
||||
connect(_prioMuxer, &PriorityMuxer::autoSelectChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
if(type == "imageToLedMapping-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "adjustment-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCB::handleAdjustmentChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCB::handleAdjustmentChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCB::handleAdjustmentChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "videomode-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::newVideoMode, this, &JsonCB::handleVideoModeChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::newVideoMode, this, &JsonCB::handleVideoModeChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::newVideoMode, this, &JsonCB::handleVideoModeChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "effects-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCB::handleEffectListChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCB::handleEffectListChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCB::handleEffectListChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "settings-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "leds-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleLedsConfigChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleLedsConfigChange);
|
||||
else
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleLedsConfigChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
|
||||
if(type == "instance-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange, Qt::UniqueConnection);
|
||||
if(unsubscribe)
|
||||
disconnect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange);
|
||||
else
|
||||
connect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void JsonCB::resetSubscriptions(void){
|
||||
for(const auto & entry : getSubscribedCommands()){
|
||||
subscribeFor(entry, true);
|
||||
}
|
||||
}
|
||||
|
||||
void JsonCB::setSubscriptionsTo(Hyperion* hyperion){
|
||||
// get current subs
|
||||
QStringList currSubs(getSubscribedCommands());
|
||||
|
||||
// stop subs
|
||||
resetSubscriptions();
|
||||
|
||||
// update pointer
|
||||
_hyperion = hyperion;
|
||||
_componentRegister = &_hyperion->getComponentRegister();
|
||||
_prioMuxer = _hyperion->getMuxerInstance();
|
||||
|
||||
// re-apply subs
|
||||
for(const auto & entry : currSubs)
|
||||
{
|
||||
subscribeFor(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void JsonCB::doCallback(const QString& cmd, const QVariant& data)
|
||||
{
|
||||
QJsonObject obj;
|
||||
|
@ -12,14 +12,16 @@ AuthManager* AuthManager::manager = nullptr;
|
||||
|
||||
AuthManager::AuthManager(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _authTable(new AuthTable(this))
|
||||
, _authTable(new AuthTable("",this))
|
||||
, _metaTable(new MetaTable(this))
|
||||
, _pendingRequests()
|
||||
, _authRequired(true)
|
||||
, _timer(new QTimer(this))
|
||||
, _authBlockTimer(new QTimer(this))
|
||||
{
|
||||
AuthManager::manager = this;
|
||||
|
||||
|
||||
// get uuid
|
||||
_uuid = _metaTable->getUUID();
|
||||
|
||||
@ -27,21 +29,18 @@ AuthManager::AuthManager(QObject* parent)
|
||||
_timer->setInterval(1000);
|
||||
connect(_timer, &QTimer::timeout, this, &AuthManager::checkTimeout);
|
||||
|
||||
// setup authBlockTimer
|
||||
_authBlockTimer->setInterval(60000);
|
||||
connect(_authBlockTimer, &QTimer::timeout, this, &AuthManager::checkAuthBlockTimeout);
|
||||
|
||||
// init with default user and password
|
||||
if(!_authTable->userExist("Hyperion"))
|
||||
{
|
||||
_authTable->createUser("Hyperion","hyperion");
|
||||
}
|
||||
}
|
||||
|
||||
bool & AuthManager::isAuthRequired()
|
||||
{
|
||||
return _authRequired;
|
||||
}
|
||||
|
||||
bool & AuthManager::isLocalAuthRequired()
|
||||
{
|
||||
return _localAuthRequired;
|
||||
// update Hyperion user token on startup
|
||||
_authTable->setUserToken("Hyperion");
|
||||
}
|
||||
|
||||
const AuthManager::AuthDefinition AuthManager::createToken(const QString& comment)
|
||||
@ -77,14 +76,69 @@ const QVector<AuthManager::AuthDefinition> AuthManager::getTokenList()
|
||||
return finalVec;
|
||||
}
|
||||
|
||||
const QString AuthManager::getUserToken(const QString & usr)
|
||||
{
|
||||
return QString(_authTable->getUserToken(usr));
|
||||
}
|
||||
|
||||
void AuthManager::setAuthBlock(const bool& user)
|
||||
{
|
||||
// current timestamp +10 minutes
|
||||
if(user)
|
||||
_userAuthAttempts.append(QDateTime::currentMSecsSinceEpoch()+600000);
|
||||
else
|
||||
_tokenAuthAttempts.append(QDateTime::currentMSecsSinceEpoch()+600000);
|
||||
|
||||
QMetaObject::invokeMethod(_authBlockTimer, "start", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
bool AuthManager::isUserAuthorized(const QString& user, const QString& pw)
|
||||
{
|
||||
return _authTable->isUserAuthorized(user, pw);
|
||||
if(isUserAuthBlocked())
|
||||
return false;
|
||||
|
||||
if(!_authTable->isUserAuthorized(user, pw)){
|
||||
setAuthBlock(true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AuthManager::isTokenAuthorized(const QString& token)
|
||||
{
|
||||
return _authTable->tokenExist(token);
|
||||
if(isTokenAuthBlocked())
|
||||
return false;
|
||||
|
||||
if(!_authTable->tokenExist(token)){
|
||||
setAuthBlock();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AuthManager::isUserTokenAuthorized(const QString& usr, const QString& token)
|
||||
{
|
||||
if(isUserAuthBlocked())
|
||||
return false;
|
||||
|
||||
if(!_authTable->isUserTokenAuthorized(usr, token)){
|
||||
setAuthBlock(true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AuthManager::updateUserPassword(const QString& user, const QString& pw, const QString& newPw)
|
||||
{
|
||||
if(isUserAuthorized(user, pw))
|
||||
return _authTable->updateUserPassword(user, newPw);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AuthManager::resetHyperionUser()
|
||||
{
|
||||
return _authTable->resetHyperionUser();
|
||||
}
|
||||
|
||||
void AuthManager::setNewTokenRequest(QObject* caller, const QString& comment, const QString& id)
|
||||
@ -144,6 +198,7 @@ void AuthManager::handleSettingsUpdate(const settings::type& type, const QJsonDo
|
||||
const QJsonObject& obj = config.object();
|
||||
_authRequired = obj["apiAuth"].toBool(true);
|
||||
_localAuthRequired = obj["localApiAuth"].toBool(false);
|
||||
_localAdminAuthRequired = obj["localAdminAuth"].toBool(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,3 +222,25 @@ void AuthManager::checkTimeout()
|
||||
if(_pendingRequests.isEmpty())
|
||||
_timer->stop();
|
||||
}
|
||||
|
||||
void AuthManager::checkAuthBlockTimeout(){
|
||||
// handle user auth block
|
||||
for (auto it = _userAuthAttempts.begin(); it != _userAuthAttempts.end(); it++) {
|
||||
// after 10 minutes, we remove the entry
|
||||
if (*it < (uint64_t)QDateTime::currentMSecsSinceEpoch()) {
|
||||
_userAuthAttempts.erase(it--);
|
||||
}
|
||||
}
|
||||
|
||||
// handle token auth block
|
||||
for (auto it = _tokenAuthAttempts.begin(); it != _tokenAuthAttempts.end(); it++) {
|
||||
// after 10 minutes, we remove the entry
|
||||
if (*it < (uint64_t)QDateTime::currentMSecsSinceEpoch()) {
|
||||
_tokenAuthAttempts.erase(it--);
|
||||
}
|
||||
}
|
||||
|
||||
// if the lists are empty we stop
|
||||
if(_userAuthAttempts.empty() && _tokenAuthAttempts.empty())
|
||||
_authBlockTimer->stop();
|
||||
}
|
||||
|
@ -66,6 +66,14 @@
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 5
|
||||
},
|
||||
"localAdminAuth" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_net_localAdminAuth_title",
|
||||
"required" : true,
|
||||
"default" : true,
|
||||
"propertyOrder" : 5
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
|
@ -17,7 +17,10 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket, const bool& local
|
||||
// create a new instance of JsonAPI
|
||||
_jsonAPI = new JsonAPI(socket->peerAddress().toString(), _log, localConnection, this);
|
||||
// get the callback messages from JsonAPI and send it to the client
|
||||
connect(_jsonAPI,SIGNAL(callbackMessage(QJsonObject)),this,SLOT(sendMessage(QJsonObject)));
|
||||
connect(_jsonAPI, &JsonAPI::callbackMessage, this , &JsonClientConnection::sendMessage);
|
||||
connect(_jsonAPI, &JsonAPI::forceClose, this , [&](){ _socket->close(); } );
|
||||
|
||||
_jsonAPI->initialize();
|
||||
}
|
||||
|
||||
void JsonClientConnection::readRequest()
|
||||
|
@ -300,8 +300,8 @@ void QtHttpClientWrapper::onReplySendDataRequested (void)
|
||||
|
||||
void QtHttpClientWrapper::sendToClientWithReply(QtHttpReply * reply)
|
||||
{
|
||||
connect (reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested);
|
||||
connect (reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested);
|
||||
connect (reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested, Qt::UniqueConnection);
|
||||
connect (reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested, Qt::UniqueConnection);
|
||||
m_parsingStatus = sendReplyToClient (reply);
|
||||
}
|
||||
|
||||
@ -340,3 +340,19 @@ QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHtt
|
||||
|
||||
return AwaitingRequest;
|
||||
}
|
||||
|
||||
void QtHttpClientWrapper::closeConnection()
|
||||
{
|
||||
// probably filter for request to follow http spec
|
||||
if(m_currentRequest != Q_NULLPTR)
|
||||
{
|
||||
QtHttpReply reply(m_serverHandle);
|
||||
reply.setStatusCode(QtHttpReply::StatusCode::Forbidden);
|
||||
|
||||
connect (&reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested, Qt::UniqueConnection);
|
||||
connect (&reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested, Qt::UniqueConnection);
|
||||
|
||||
m_parsingStatus = sendReplyToClient(&reply);
|
||||
}
|
||||
m_sockClient->close ();
|
||||
}
|
||||
|
@ -34,6 +34,11 @@ public:
|
||||
/// @brief Wrapper for sendReplyToClient(), handles m_parsingStatus and signal connect
|
||||
void sendToClientWithReply (QtHttpReply * reply);
|
||||
|
||||
///
|
||||
/// @brief close a connection with FORBIDDEN header (used from JsonAPI over HTTP)
|
||||
///
|
||||
void closeConnection();
|
||||
|
||||
private slots:
|
||||
void onClientDataReceived (void);
|
||||
|
||||
|
@ -15,14 +15,20 @@ WebJsonRpc::WebJsonRpc(QtHttpRequest* request, QtHttpServer* server, const bool&
|
||||
const QString client = request->getClientInfo().clientAddress.toString();
|
||||
_jsonAPI = new JsonAPI(client, _log, localConnection, this, true);
|
||||
connect(_jsonAPI, &JsonAPI::callbackMessage, this, &WebJsonRpc::handleCallback);
|
||||
connect(_jsonAPI, &JsonAPI::forceClose, [&]() { _wrapper->closeConnection(); _stopHandle = true; });
|
||||
_jsonAPI->initialize();
|
||||
}
|
||||
|
||||
void WebJsonRpc::handleMessage(QtHttpRequest* request)
|
||||
{
|
||||
QByteArray header = request->getHeader("Authorization");
|
||||
QByteArray data = request->getRawData();
|
||||
_unlocked = true;
|
||||
_jsonAPI->handleMessage(data,header);
|
||||
// TODO better solution. If jsonAPI emits forceClose the request is deleted and the following call to this method results in segfault
|
||||
if(!_stopHandle)
|
||||
{
|
||||
QByteArray header = request->getHeader("Authorization");
|
||||
QByteArray data = request->getRawData();
|
||||
_unlocked = true;
|
||||
_jsonAPI->handleMessage(data,header);
|
||||
}
|
||||
}
|
||||
|
||||
void WebJsonRpc::handleCallback(QJsonObject obj)
|
||||
|
@ -22,6 +22,7 @@ private:
|
||||
Logger* _log;
|
||||
JsonAPI* _jsonAPI;
|
||||
|
||||
bool _stopHandle = false;
|
||||
bool _unlocked = false;
|
||||
|
||||
private slots:
|
||||
|
@ -25,6 +25,7 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, const
|
||||
// Json processor
|
||||
_jsonAPI = new JsonAPI(client, _log, localConnection, this);
|
||||
connect(_jsonAPI, &JsonAPI::callbackMessage, this, &WebSocketClient::sendMessage);
|
||||
connect(_jsonAPI, &JsonAPI::forceClose, this,[this]() { this->sendClose(CLOSECODE::NORMAL); });
|
||||
|
||||
Debug(_log, "New connection from %s", QSTRING_CSTR(client));
|
||||
|
||||
@ -40,6 +41,9 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, const
|
||||
|
||||
_socket->write(QSTRING_CSTR(data), data.size());
|
||||
_socket->flush();
|
||||
|
||||
// Init JsonAPI
|
||||
_jsonAPI->initialize();
|
||||
}
|
||||
|
||||
void WebSocketClient::handleWebSocketFrame(void)
|
||||
@ -123,7 +127,7 @@ void WebSocketClient::handleWebSocketFrame(void)
|
||||
handleBinaryMessage(_wsReceiveBuffer);
|
||||
}
|
||||
_wsReceiveBuffer.clear();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -72,7 +72,7 @@ class HyperionDaemon : public QObject
|
||||
friend SysTray;
|
||||
|
||||
public:
|
||||
HyperionDaemon(QString rootPath, QObject *parent, const bool& logLvlOverwrite );
|
||||
HyperionDaemon(QString rootPath, QObject *parent, const bool& logLvlOverwrite);
|
||||
~HyperionDaemon();
|
||||
|
||||
///
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <utils/FileUtils.h>
|
||||
#include <commandline/Parser.h>
|
||||
#include <commandline/IntOption.h>
|
||||
#include <../../include/db/AuthTable.h>
|
||||
|
||||
#ifdef ENABLE_X11
|
||||
#include <X11/Xlib.h>
|
||||
@ -239,14 +240,15 @@ int main(int argc, char** argv)
|
||||
Parser parser("Hyperion Daemon");
|
||||
parser.addHelpOption();
|
||||
|
||||
BooleanOption & versionOption = parser.add<BooleanOption>(0x0, "version", "Show version information");
|
||||
Option & userDataOption = parser.add<Option> (0x0, "userdata", "Overwrite user data path, defaults to home directory of current user (%1)", QDir::homePath() + "/.hyperion");
|
||||
BooleanOption & silentOption = parser.add<BooleanOption>('s', "silent", "do not print any outputs");
|
||||
BooleanOption & verboseOption = parser.add<BooleanOption>('v', "verbose", "Increase verbosity");
|
||||
BooleanOption & debugOption = parser.add<BooleanOption>('d', "debug", "Show debug messages");
|
||||
parser.add<BooleanOption>(0x0, "desktop", "show systray on desktop");
|
||||
parser.add<BooleanOption>(0x0, "service", "force hyperion to start as console service");
|
||||
Option & exportEfxOption = parser.add<Option> (0x0, "export-effects", "export effects to given path");
|
||||
BooleanOption & versionOption = parser.add<BooleanOption> (0x0, "version", "Show version information");
|
||||
Option & userDataOption = parser.add<Option> ('u', "userdata", "Overwrite user data path, defaults to home directory of current user (%1)", QDir::homePath() + "/.hyperion");
|
||||
BooleanOption & resetPassword = parser.add<BooleanOption> (0x0, "resetPassword", "Lost your password? Reset it with this option back to 'hyperion'");
|
||||
BooleanOption & silentOption = parser.add<BooleanOption> ('s', "silent", "do not print any outputs");
|
||||
BooleanOption & verboseOption = parser.add<BooleanOption> ('v', "verbose", "Increase verbosity");
|
||||
BooleanOption & debugOption = parser.add<BooleanOption> ('d', "debug", "Show debug messages");
|
||||
parser.add<BooleanOption> (0x0, "desktop", "show systray on desktop");
|
||||
parser.add<BooleanOption> (0x0, "service", "force hyperion to start as console service");
|
||||
Option & exportEfxOption = parser.add<Option> (0x0, "export-effects", "export effects to given path");
|
||||
|
||||
parser.process(*qApp);
|
||||
|
||||
@ -334,6 +336,21 @@ int main(int argc, char** argv)
|
||||
|
||||
Info(log, "Set user data path to '%s'", QSTRING_CSTR(mDir.absolutePath()));
|
||||
|
||||
// reset Password without spawning daemon
|
||||
if(parser.isSet(resetPassword))
|
||||
{
|
||||
AuthTable* table = new AuthTable(userDataPath);
|
||||
if(table->resetHyperionUser()){
|
||||
Info(log,"Password reset successfull");
|
||||
delete table;
|
||||
exit(0);
|
||||
} else {
|
||||
Error(log,"Failed to reset password!");
|
||||
delete table;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
HyperionDaemon* hyperiond = nullptr;
|
||||
try
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user