Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
54 changes: 54 additions & 0 deletions src/confighttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "nvhttp.h"
#include "platform/common.h"
#include "process.h"
#include "rtsp.h"
#include "utility.h"
#include "uuid.h"

Expand Down Expand Up @@ -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": "<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.
* @param response The HTTP response object.
Expand Down Expand Up @@ -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;
Expand Down
35 changes: 35 additions & 0 deletions src/nvhttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ namespace nvhttp {
std::string name;
std::string uuid;
std::string cert;
bool enabled = true;
};

struct client_t {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -253,6 +255,7 @@ namespace nvhttp {
named_cert.name = el.get_child("name").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.enabled = el.get<bool>("enabled", true);
client.named_devices.emplace_back(named_cert);
}
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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<bool>(mail::shutdown);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
8 changes: 8 additions & 0 deletions src/nvhttp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
24 changes: 22 additions & 2 deletions src_assets/common/assets/web/troubleshooting.html
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,17 @@ <h2 id="unpair" class="mb-0">{{ $t('troubleshooting.unpair_title') }}</h2>
</div>
<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">
<div class="flex-grow-1">{{ client.name !== "" ? client.name : $t('troubleshooting.unpair_single_unknown') }}</div>
<button class="btn btn-danger ms-auto" @click="unpairSingle(client.uuid)">
<div class="flex-grow-1">
{{ 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>
</button>
</li>
Expand Down Expand Up @@ -455,6 +464,17 @@ <h2 id="logs">{{ $t('troubleshooting.logs') }}</h2>
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())
Expand Down
Loading