Skip to content

Commit

Permalink
Refactor Hyperion JSON-API (#1727)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lord-Grey authored May 8, 2024
1 parent 94850d8 commit cf287f5
Show file tree
Hide file tree
Showing 64 changed files with 4,184 additions and 2,943 deletions.
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Breaking

**JSON-API**
- Align JSON subscription update elements. `ledcolors-imagestream-update, ledcolors-ledstream-update, logmsg-update` now return data via `data` and not `result

### Added

- Support gaps on Matrix Layout (#1696)

**JSON-API**
- New subscription support for event updates, i.e. `Suspend, Resume, Idle, idleResume, Restart, Quit`.
- Support direct or multiple instance addressing via single requests (#809)
- Support of `serverinfo` subcommands: `getInfo, subscribe, unsubscribe, getSubscriptions, getSubscriptionCommands`
- [Overview](https://github.com/hyperion-project/hyperion.ng/blob/API_Auth/doc/development/JSON-API%20_Commands_Overview.md) of API commands and subscription updates

### Changed

- Fixed: Cross Site Scripting Vulnerability (CVE-2024-4174, CVE-2024-4175)
- Fixed: hyperion-v4l2 taking screenshot failed (#1722)
- Nanoleaf: Support new devices and do not restore ExtControl state
- Workaround to address Web UI keeps forcing browser to download the html instead (#1692)
- Fixed: Kodi Color Calibration, Refactor Wizards (#1674)
- Fixed: Token Dialog not closing

**JSON-API**
- Refactored JSON-API to ensure consistent authorization behaviour across sessions and single requests with token authorization.
- Provide additional error details with API responses, esp. on JSON parsing, validation or token errors.
- Generate random TANs for every API request from the Hyperion UI
- Fixed: Handling of IP4 addresses wrapped in IPv6 for external network connections-

### Removed

**JSON-API**
- Removed ability to enable/disable local admin authorization. All admin commands require authorization, i.e. `authorize-adminRequired` will always be `true`.
- Removed `session-updates` subscription
- `serverinfo/subscribe` element will be deprecated and replaced by corresponding subcommand

## [2.0.16](https://github.com/hyperion-project/hyperion.ng/releases/tag/2.0.16) - 2024-01

### Added
Expand Down
16 changes: 7 additions & 9 deletions assets/webconfig/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
"conf_network_json_intro": "The JSON-RPC-Port of all Hyperion instances, used for remote control.",
"conf_network_net_intro": "Network related settings which are applied to all network services.",
"conf_network_proto_intro": "The PROTO-Port of all Hyperion instances, used for picture streams (HyperionScreenCap, Kodi Addon, Android Hyperion Grabber, ...)",
"conf_network_tok_idhead": "ID",
"conf_network_tok_cidhead": "Description",
"conf_network_tok_comment_title": "Token description",
"conf_network_tok_desc": "Tokens grant other applications access to the Hyperion API, an application can request a token where you need to accept it or you create them on your own below. These tokens are just required when \"API Authorization\" is enabled in network settings.",
Expand Down Expand Up @@ -500,19 +501,16 @@
"edt_conf_log_level_expl": "Depending on loglevel you see less or more messages in your log.",
"edt_conf_log_level_title": "Log-Level",
"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_apiAuth_title": "API Authentication",
"edt_conf_net_heading_title": "Network",
"edt_conf_net_internetAccessAPI_expl": "Allow access to the Hyperion API/Webinterface from the internet. Disable for higher security.",
"edt_conf_net_internetAccessAPI_expl": "Allow access to the Hyperion API/Web Interface from the Internet. Disable for increased security.",
"edt_conf_net_internetAccessAPI_title": "Internet API Access",
"edt_conf_net_ipWhitelist_expl": "You can whitelist IP addresses instead allowing all connections from internet to connect to the Hyperion API/Webinterface.",
"edt_conf_net_ipWhitelist_title": "Whitelisted IP's",
"edt_conf_net_ipWhitelist_expl": "Define whitelisted IP addresses from which API requests from the Internet are allowed. All other external connections will be denied.",
"edt_conf_net_ipWhitelist_title": "Whitelisted IP addresses",
"edt_conf_net_ip_itemtitle": "IP",
"edt_conf_net_localAdminAuth_expl": "When enabled, administration access from your local network needs a password.",
"edt_conf_net_localAdminAuth_title": "Local Admin API Authentication",
"edt_conf_net_localApiAuth_expl": "When enabled, connections from your home network needs to authenticate themselves against Hyperion with a token.",
"edt_conf_net_localApiAuth_expl": "When disabled, API authorisation via password or token is not required for local connections. The exception is administrative commands.",
"edt_conf_net_localApiAuth_title": "Local API Authentication",
"edt_conf_net_restirctedInternetAccessAPI_expl": "You can restrict the access to the API through the internet to certain IP's.",
"edt_conf_net_restirctedInternetAccessAPI_title": "Restrict to IP's",
"edt_conf_net_restirctedInternetAccessAPI_expl": "You can restrict API requests over the Internet to only those IP addresses on the whitelist.",
"edt_conf_net_restirctedInternetAccessAPI_title": "Restrict to IP addresses",
"edt_conf_os_events_lockEnable_title": "Listen to lock events",
"edt_conf_os_events_lockEnable_expl": "Listen to screen lock/unlock events",
"edt_conf_os_events_suspendEnable_title": "Listen to suspend events",
Expand Down
53 changes: 24 additions & 29 deletions assets/webconfig/js/content_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,26 +73,30 @@ $(document).ready(function () {
//End language selection

$(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

if (event.response && event.response.info !== undefined) {
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');
}
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)
});
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) {
Expand Down Expand Up @@ -186,21 +190,12 @@ $(document).ready(function () {
}
});

$(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)
requestRequiresDefaultPasswortChange();
else
requestServerConfigSchema();
});

$(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")) {
removeStorage("loginToken");
requestRequiresAdminAuth();
requestRequiresDefaultPasswortChange();
}
else if (event.reason == "Selected Hyperion instance isn't running") {
//Switch to default instance
Expand All @@ -211,7 +206,7 @@ $(document).ready(function () {
});

$(window.hyperion).on("open", function (event) {
requestRequiresAdminAuth();
requestRequiresDefaultPasswortChange();
});

$(window.hyperion).on("ready", function (event) {
Expand Down
9 changes: 6 additions & 3 deletions assets/webconfig/js/content_logging.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ var createdCont = false;
var isScroll = true;

performTranslation();
requestLoggingStop();

$(document).ready(function () {

window.addEventListener('hashchange', function(event) {
requestLoggingStop();
});

requestLoggingStart();

$('#conf_cont').append(createOptPanel('fa-reorder', $.i18n("edt_conf_log_heading_title"), 'editor_container', 'btn_submit'));
Expand Down Expand Up @@ -178,9 +181,9 @@ $(document).ready(function () {
if (!window.loggingHandlerInstalled) {
window.loggingHandlerInstalled = true;

$(window.hyperion).on("cmd-logging-update", function (event) {
$(window.hyperion).on("cmd-logmsg-update", function (event) {

var messages = (event.response.result.messages);
var messages = (event.response.data.messages);

if (messages.length != 0) {
if (!createdCont) {
Expand Down
4 changes: 2 additions & 2 deletions assets/webconfig/js/content_network.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,13 @@ $(document).ready(function () {
for (var key in tokenList) {
var lastUse = (tokenList[key].last_use) ? tokenList[key].last_use : "-";
var btn = '<button id="tok' + tokenList[key].id + '" type="button" class="btn btn-danger">' + $.i18n('general_btn_delete') + '</button>';
$('.tktbody').append(createTableRow([tokenList[key].comment, lastUse, btn], false, true));
$('.tktbody').append(createTableRow([tokenList[key].id, tokenList[key].comment, lastUse, btn], false, true));
$('#tok' + tokenList[key].id).off().on('click', handleDeleteToken);
}
}

createTable('tkthead', 'tktbody', 'tktable');
$('.tkthead').html(createTableRow([$.i18n('conf_network_tok_cidhead'), $.i18n('conf_network_tok_lastuse'), $.i18n('general_btn_delete')], true, true));
$('.tkthead').html(createTableRow([$.i18n('conf_network_tok_idhead'), $.i18n('conf_network_tok_cidhead'), $.i18n('conf_network_tok_lastuse'), $.i18n('general_btn_delete')], true, true));
buildTokenList();

function handleDeleteToken(e) {
Expand Down
15 changes: 8 additions & 7 deletions assets/webconfig/js/hyperion.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ function sendToHyperion(command, subcommand, msg)
else
msg = "";

window.wsTan = Math.floor(Math.random() * 1000)
window.websocket.send('{"command":"'+command+'", "tan":'+window.wsTan+subcommand+msg+'}');
}

Expand All @@ -187,7 +188,7 @@ function sendToHyperion(command, subcommand, msg)
// data: The json data as Object
// tan: The optional tan, default 1. If the tan is -1, we skip global response error handling
// Returns data of response or false if timeout
async function sendAsyncToHyperion (command, subcommand, data, tan = 1) {
async function sendAsyncToHyperion (command, subcommand, data, tan = Math.floor(Math.random() * 1000) ) {
let obj = { command, tan }
if (subcommand) {Object.assign(obj, {subcommand})}
if (data) { Object.assign(obj, data) }
Expand Down Expand Up @@ -486,38 +487,38 @@ async function requestLedDeviceDiscovery(type, params)
{
let data = { ledDeviceType: type, params: params };

return sendAsyncToHyperion("leddevice", "discover", data, Math.floor(Math.random() * 1000) );
return sendAsyncToHyperion("leddevice", "discover", data);
}

async function requestLedDeviceProperties(type, params)
{
let data = { ledDeviceType: type, params: params };

return sendAsyncToHyperion("leddevice", "getProperties", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("leddevice", "getProperties", data);
}

function requestLedDeviceIdentification(type, params)
{
let data = { ledDeviceType: type, params: params };

return sendAsyncToHyperion("leddevice", "identify", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("leddevice", "identify", data);
}

async function requestLedDeviceAddAuthorization(type, params) {
let data = { ledDeviceType: type, params: params };

return sendAsyncToHyperion("leddevice", "addAuthorization", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("leddevice", "addAuthorization", data);
}

async function requestInputSourcesDiscovery(type, params) {
let data = { sourceType: type, params: params };

return sendAsyncToHyperion("inputsource", "discover", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("inputsource", "discover", data);
}

async function requestServiceDiscovery(type, params) {
let data = { serviceType: type, params: params };

return sendAsyncToHyperion("service", "discover", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("service", "discover", data);
}

4 changes: 2 additions & 2 deletions assets/webconfig/js/ledsim.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ $(document).ready(function () {
$("body").get(0).style.setProperty("--background-var", "none");
}
else {
printLedsToCanvas(event.response.result.leds)
printLedsToCanvas(event.response.data.leds)
$("body").get(0).style.setProperty("--background-var", "url(" + ($('#leds_preview_canv')[0]).toDataURL("image/jpg") + ") no-repeat top left");
}
});
Expand All @@ -275,7 +275,7 @@ $(document).ready(function () {
}
}
else {
var imageData = (event.response.result.image);
var imageData = (event.response.data.image);

var image = new Image();
image.onload = function () {
Expand Down
6 changes: 3 additions & 3 deletions assets/webconfig/js/ui_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,9 @@ function showInfoDialog(type, header, message) {
});

$(document).on('click', '[data-dismiss-modal]', function () {
var target = $(this).attr('data-dismiss-modal');
$.find(target).modal('hide');
});
var target = $(this).data('dismiss-modal');
$($.find(target)).modal('hide');
});
}

function createHintH(type, text, container) {
Expand Down
4 changes: 1 addition & 3 deletions config/hyperion.config.json.default
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,7 @@
"internetAccessAPI": false,
"restirctedInternetAccessAPI": false,
"ipWhitelist": [],
"apiAuth": true,
"localApiAuth": false,
"localAdminAuth": true
"localApiAuth": false
},

"ledConfig": {
Expand Down
Loading

0 comments on commit cf287f5

Please sign in to comment.