feat(api/ui): add client enable/disable access control (#4771)
This commit is contained in:
@@ -61,6 +61,9 @@ curl -u user:pass -H "X-CSRF-Token: your_token_here" \
|
|||||||
## POST /api/clients/unpair-all
|
## POST /api/clients/unpair-all
|
||||||
@copydoc confighttp::unpairAll()
|
@copydoc confighttp::unpairAll()
|
||||||
|
|
||||||
|
## POST /api/clients/update
|
||||||
|
@copydoc confighttp::updateClient()
|
||||||
|
|
||||||
## GET /api/config
|
## GET /api/config
|
||||||
@copydoc confighttp::getConfig()
|
@copydoc confighttp::getConfig()
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#include "nvhttp.h"
|
#include "nvhttp.h"
|
||||||
#include "platform/common.h"
|
#include "platform/common.h"
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
|
#include "rtsp.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "uuid.h"
|
#include "uuid.h"
|
||||||
|
|
||||||
@@ -843,6 +844,58 @@ namespace confighttp {
|
|||||||
send_response(response, output_tree);
|
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": "<uuid>",
|
||||||
|
* "enabled": true
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @api_examples{/api/clients/update| POST| {"uuid":"<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.
|
* @brief Unpair a client.
|
||||||
* @param response The HTTP response object.
|
* @param response The HTTP response object.
|
||||||
@@ -1716,6 +1769,7 @@ namespace confighttp {
|
|||||||
server.resource["^/api/clients/list$"]["GET"] = getClients;
|
server.resource["^/api/clients/list$"]["GET"] = getClients;
|
||||||
server.resource["^/api/clients/unpair$"]["POST"] = unpair;
|
server.resource["^/api/clients/unpair$"]["POST"] = unpair;
|
||||||
server.resource["^/api/clients/unpair-all$"]["POST"] = unpairAll;
|
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$"]["GET"] = getConfig;
|
||||||
server.resource["^/api/config$"]["POST"] = saveConfig;
|
server.resource["^/api/config$"]["POST"] = saveConfig;
|
||||||
server.resource["^/api/configLocale$"]["GET"] = getLocale;
|
server.resource["^/api/configLocale$"]["GET"] = getLocale;
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ namespace nvhttp {
|
|||||||
std::string name;
|
std::string name;
|
||||||
std::string uuid;
|
std::string uuid;
|
||||||
std::string cert;
|
std::string cert;
|
||||||
|
bool enabled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct client_t {
|
struct client_t {
|
||||||
@@ -190,6 +191,7 @@ namespace nvhttp {
|
|||||||
named_cert_node.put("name"s, named_cert.name);
|
named_cert_node.put("name"s, named_cert.name);
|
||||||
named_cert_node.put("cert"s, named_cert.cert);
|
named_cert_node.put("cert"s, named_cert.cert);
|
||||||
named_cert_node.put("uuid"s, named_cert.uuid);
|
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));
|
named_cert_nodes.push_back(std::make_pair(""s, named_cert_node));
|
||||||
}
|
}
|
||||||
root.add_child("root.named_devices"s, named_cert_nodes);
|
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<std::string>();
|
named_cert.name = el.get_child("name").get_value<std::string>();
|
||||||
named_cert.cert = el.get_child("cert").get_value<std::string>();
|
named_cert.cert = el.get_child("cert").get_value<std::string>();
|
||||||
named_cert.uuid = el.get_child("uuid").get_value<std::string>();
|
named_cert.uuid = el.get_child("uuid").get_value<std::string>();
|
||||||
|
named_cert.enabled = el.get<bool>("enabled", true);
|
||||||
client.named_devices.emplace_back(named_cert);
|
client.named_devices.emplace_back(named_cert);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -777,6 +780,7 @@ namespace nvhttp {
|
|||||||
nlohmann::json named_cert_node;
|
nlohmann::json named_cert_node;
|
||||||
named_cert_node["name"] = named_cert.name;
|
named_cert_node["name"] = named_cert.name;
|
||||||
named_cert_node["uuid"] = named_cert.uuid;
|
named_cert_node["uuid"] = named_cert.uuid;
|
||||||
|
named_cert_node["enabled"] = named_cert.enabled;
|
||||||
named_cert_nodes.push_back(named_cert_node);
|
named_cert_nodes.push_back(named_cert_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1056,6 +1060,8 @@ namespace nvhttp {
|
|||||||
conf_intern.servercert = cert;
|
conf_intern.servercert = cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_client_enabled(const std::string_view cert_pem);
|
||||||
|
|
||||||
void start() {
|
void start() {
|
||||||
platf::set_thread_name("nvhttp");
|
platf::set_thread_name("nvhttp");
|
||||||
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
|
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
|
||||||
@@ -1124,6 +1130,13 @@ namespace nvhttp {
|
|||||||
return verified;
|
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;
|
verified = 1;
|
||||||
|
|
||||||
return verified;
|
return verified;
|
||||||
@@ -1225,4 +1238,26 @@ namespace nvhttp {
|
|||||||
load_state();
|
load_state();
|
||||||
return removed;
|
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
|
} // namespace nvhttp
|
||||||
|
|||||||
@@ -184,6 +184,14 @@ namespace nvhttp {
|
|||||||
*/
|
*/
|
||||||
bool unpair_client(std::string_view uuid);
|
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.
|
* @brief Get all paired clients.
|
||||||
* @return The list of all paired clients.
|
* @return The list of all paired clients.
|
||||||
|
|||||||
@@ -135,8 +135,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<ul class="list-group list-group-flush" v-if="clients && clients.length > 0">
|
<ul class="list-group list-group-flush" v-if="clients && clients.length > 0">
|
||||||
<li v-for="client in clients" :key="client.uuid" class="list-group-item d-flex align-items-center">
|
<li v-for="client in clients" :key="client.uuid" class="list-group-item d-flex align-items-center">
|
||||||
<div class="flex-grow-1">{{ client.name !== "" ? client.name : $t('troubleshooting.unpair_single_unknown') }}</div>
|
<div class="flex-grow-1">
|
||||||
<button class="btn btn-danger ms-auto" @click="unpairSingle(client.uuid)">
|
{{ client.name !== "" ? client.name : $t('troubleshooting.unpair_single_unknown') }}
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-switch ms-2 mb-0">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch"
|
||||||
|
:id="'toggle-' + client.uuid"
|
||||||
|
:checked="client.enabled"
|
||||||
|
:aria-checked="client.enabled.toString()"
|
||||||
|
@change="toggleClient(client.uuid, !client.enabled)">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-danger btn-sm ms-2" @click="unpairSingle(client.uuid)">
|
||||||
<trash-2 :size="18" class="icon"></trash-2>
|
<trash-2 :size="18" class="icon"></trash-2>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
@@ -455,6 +464,17 @@
|
|||||||
this.refreshClients();
|
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() {
|
refreshClients() {
|
||||||
fetch("./api/clients/list")
|
fetch("./api/clients/list")
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
|
|||||||
Reference in New Issue
Block a user