diff --git a/docs/api.md b/docs/api.md index b41699f77dc..c62545b6edb 100644 --- a/docs/api.md +++ b/docs/api.md @@ -61,6 +61,9 @@ curl -u user:pass -H "X-CSRF-Token: your_token_here" \ ## POST /api/clients/unpair-all @copydoc confighttp::unpairAll() +## POST /api/clients/update +@copydoc confighttp::updateClient() + ## GET /api/config @copydoc confighttp::getConfig() diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 4a0bb128016..37be48b7c50 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -41,6 +41,7 @@ #include "nvhttp.h" #include "platform/common.h" #include "process.h" +#include "rtsp.h" #include "utility.h" #include "uuid.h" @@ -843,6 +844,58 @@ namespace confighttp { send_response(response, output_tree); } + /** + * @brief Enable or disable a client. + * @param response The HTTP response object. + * @param request The HTTP request object. + * The body for the POST request should be JSON serialized in the following format: + * @code{.json} + * { + * "uuid": "", + * "enabled": true + * } + * @endcode + * + * @api_examples{/api/clients/update| POST| {"uuid":"","enabled":true}} + */ + void updateClient(resp_https_t response, req_https_t request) { + if (!check_content_type(response, request, "application/json")) { + return; + } + if (!authenticate(response, request)) { + return; + } + std::string client_id = get_client_id(request); + if (!validate_csrf_token(response, request, client_id)) { + return; + } + + print_req(request); + + std::stringstream ss; + ss << request->content.rdbuf(); + try { + nlohmann::json input_tree = nlohmann::json::parse(ss.str()); + nlohmann::json output_tree; + std::string uuid = input_tree.value("uuid", ""); + bool enabled = input_tree.value("enabled", true); + output_tree["status"] = nvhttp::set_client_enabled(uuid, enabled); + + if (!enabled && output_tree["status"]) { + rtsp_stream::terminate_sessions(); + + if (rtsp_stream::session_count() == 0 && proc::proc.running() > 0) { + proc::proc.terminate(); + } + } + + send_response(response, output_tree); + } catch (nlohmann::json::exception &e) { + BOOST_LOG(warning) << "Update Client: "sv << e.what(); + bad_request(response, request, e.what()); + } + } + /** * @brief Unpair a client. * @param response The HTTP response object. @@ -1716,6 +1769,7 @@ namespace confighttp { server.resource["^/api/clients/list$"]["GET"] = getClients; server.resource["^/api/clients/unpair$"]["POST"] = unpair; server.resource["^/api/clients/unpair-all$"]["POST"] = unpairAll; + server.resource["^/api/clients/update$"]["POST"] = updateClient; server.resource["^/api/config$"]["GET"] = getConfig; server.resource["^/api/config$"]["POST"] = saveConfig; server.resource["^/api/configLocale$"]["GET"] = getLocale; diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index b4f721a4c93..03c1e26aaa8 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -132,6 +132,7 @@ namespace nvhttp { std::string name; std::string uuid; std::string cert; + bool enabled = true; }; struct client_t { @@ -190,6 +191,7 @@ namespace nvhttp { named_cert_node.put("name"s, named_cert.name); named_cert_node.put("cert"s, named_cert.cert); named_cert_node.put("uuid"s, named_cert.uuid); + named_cert_node.put("enabled"s, named_cert.enabled); named_cert_nodes.push_back(std::make_pair(""s, named_cert_node)); } root.add_child("root.named_devices"s, named_cert_nodes); @@ -253,6 +255,7 @@ namespace nvhttp { named_cert.name = el.get_child("name").get_value(); named_cert.cert = el.get_child("cert").get_value(); named_cert.uuid = el.get_child("uuid").get_value(); + named_cert.enabled = el.get("enabled", true); client.named_devices.emplace_back(named_cert); } } @@ -777,6 +780,7 @@ namespace nvhttp { nlohmann::json named_cert_node; named_cert_node["name"] = named_cert.name; named_cert_node["uuid"] = named_cert.uuid; + named_cert_node["enabled"] = named_cert.enabled; named_cert_nodes.push_back(named_cert_node); } @@ -1056,6 +1060,8 @@ namespace nvhttp { conf_intern.servercert = cert; } + bool is_client_enabled(const std::string_view cert_pem); + void start() { platf::set_thread_name("nvhttp"); auto shutdown_event = mail::man->event(mail::shutdown); @@ -1124,6 +1130,13 @@ namespace nvhttp { return verified; } + // Check if this client is enabled + auto pem = crypto::pem(x509); + if (!is_client_enabled(pem)) { + BOOST_LOG(info) << "Client is disabled -- denied"sv; + return verified; + } + verified = 1; return verified; @@ -1225,4 +1238,26 @@ namespace nvhttp { load_state(); return removed; } + + bool set_client_enabled(const std::string_view uuid, bool enabled) { + client_t &client = client_root; + for (auto &named_cert : client.named_devices) { + if (named_cert.uuid == uuid) { + named_cert.enabled = enabled; + save_state(); + return true; + } + } + return false; + } + + bool is_client_enabled(const std::string_view cert_pem) { + const client_t &client = client_root; + for (const auto &named_cert : client.named_devices) { + if (named_cert.cert == cert_pem) { + return named_cert.enabled; + } + } + return true; + } } // namespace nvhttp diff --git a/src/nvhttp.h b/src/nvhttp.h index 636337071b9..c24d018c969 100644 --- a/src/nvhttp.h +++ b/src/nvhttp.h @@ -184,6 +184,14 @@ namespace nvhttp { */ bool unpair_client(std::string_view uuid); + /** + * @brief Enable or disable a client. + * @param uuid The UUID of the client. + * @param enabled Whether the client should be enabled. + * @return true if the client was found and updated. + */ + bool set_client_enabled(std::string_view uuid, bool enabled); + /** * @brief Get all paired clients. * @return The list of all paired clients. diff --git a/src_assets/common/assets/web/troubleshooting.html b/src_assets/common/assets/web/troubleshooting.html index d74bb204f53..33d79c934ce 100644 --- a/src_assets/common/assets/web/troubleshooting.html +++ b/src_assets/common/assets/web/troubleshooting.html @@ -135,8 +135,17 @@

{{ $t('troubleshooting.unpair_title') }}

  • -
    {{ client.name !== "" ? client.name : $t('troubleshooting.unpair_single_unknown') }}
    -
  • @@ -455,6 +464,17 @@

    {{ $t('troubleshooting.logs') }}

    this.refreshClients(); }); }, + toggleClient(uuid, enabled) { + fetch("./api/clients/update", { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ uuid, enabled }) + }).then(() => { + this.refreshClients(); + }); + }, refreshClients() { fetch("./api/clients/list") .then((response) => response.json())