Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Name and unpair individual clients #2042

Merged
merged 3 commits into from
May 27, 2024
Merged
Changes from 1 commit
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
Next Next commit
Squashed commit of the following:
commit d2e21f9
Merge: 52cb996 7fb8c76
Author: Xander Frangos <[email protected]>
Date:   Wed May 1 21:39:04 2024 -0400

    Merge branch 'nightly' into unpair-single-client

commit 52cb996
Author: Xander Frangos <[email protected]>
Date:   Thu Apr 18 22:19:53 2024 -0400

    Rename unpair_all_desc to unpair_desc

commit f5a0916
Author: Xander Frangos <[email protected]>
Date:   Thu Apr 18 22:01:50 2024 -0400

    Update src_assets/common/assets/web/troubleshooting.html

    Co-authored-by: ReenigneArcher <[email protected]>

commit e30ccb4
Author: Xander Frangos <[email protected]>
Date:   Thu Apr 18 22:01:02 2024 -0400

    Update src_assets/common/assets/web/public/assets/locale/en.json

    Co-authored-by: ReenigneArcher <[email protected]>

commit a64bfc4
Author: Xander Frangos <[email protected]>
Date:   Thu Apr 18 09:31:38 2024 -0400

    Re-use navbar.pin string

commit a390977
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 21:35:07 2024 -0400

    Additional variable reordering

commit 63d5875
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 21:32:27 2024 -0400

    Reordered Vue page vars

    #2042 (review)

commit 31c1f21
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 21:29:34 2024 -0400

    Specify that only individual unpair requires force close

commit bb3c0ba
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 21:21:35 2024 -0400

    Sort client list alphabetically

    #2042 (review)

commit 2e82537
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 21:14:05 2024 -0400

    Localize "PIN" input placeholder

    #2042 (review)

commit 1b6a828
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 21:13:02 2024 -0400

    Close session when unpairing all clients

    #2042 (review)

commit 2700152
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 13:04:20 2024 -0400

    Formatting

commit 4e77854
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 12:45:22 2024 -0400

    Rename unpair all button

commit cef0b19
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 12:42:45 2024 -0400

    Refresh client certs after unpairing a single client

commit 5ef7b03
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 12:41:14 2024 -0400

    Update web UI to use localization for single device pairing/unpairing

commit da936b8
Merge: 2452665 9c68a62
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 11:01:42 2024 -0400

    Merge branch 'nightly' into unpair-single-client

commit 9c68a62
Merge: 2511bd0 ec8170c
Author: Xander Frangos <[email protected]>
Date:   Wed Apr 17 10:43:49 2024 -0400

    Merge branch 'nightly' of https://github.com/xanderfrangos/Sunshine into nightly

commit 2452665
Merge: 735d788 65493d0
Author: Xander Frangos <[email protected]>
Date:   Sat Feb 10 18:22:42 2024 -0500

    Merge remote-tracking branch 'upstream/nightly' into unpair-single-client

commit 735d788
Author: Xander Frangos <[email protected]>
Date:   Sat Feb 10 18:09:49 2024 -0500

    Update named_cert_t to better match sunshine_state.json

commit 5ff80ad
Author: Xander Frangos <[email protected]>
Date:   Sat Feb 10 17:53:05 2024 -0500

    Remove uniqueID from client_t

commit fbd26d0
Author: Xander Frangos <[email protected]>
Date:   Sun Jan 28 11:10:17 2024 -0500

    Rename device name field and mark fields as required

    #2042 (review)

commit b5d7da4
Author: Xander Frangos <[email protected]>
Date:   Thu Jan 25 23:42:18 2024 -0500

    Update src_assets/common/assets/web/troubleshooting.html

    Co-authored-by: ReenigneArcher <[email protected]>

commit 60e2da7
Author: Xander Frangos <[email protected]>
Date:   Thu Jan 25 23:41:59 2024 -0500

    Update src_assets/common/assets/web/troubleshooting.html

    Co-authored-by: ReenigneArcher <[email protected]>

commit a4a4125
Author: Xander Frangos <[email protected]>
Date:   Thu Jan 25 23:41:46 2024 -0500

    Update src/confighttp.cpp

    Co-authored-by: ReenigneArcher <[email protected]>

commit 805311b
Author: Xander Frangos <[email protected]>
Date:   Thu Jan 25 23:41:36 2024 -0500

    Update src/confighttp.cpp

    Co-authored-by: ReenigneArcher <[email protected]>

commit cb6846e
Author: Xander Frangos <[email protected]>
Date:   Thu Jan 25 23:41:25 2024 -0500

    Update src/confighttp.cpp

    Co-authored-by: ReenigneArcher <[email protected]>

commit 3a1c109
Author: Xander Frangos <[email protected]>
Date:   Thu Jan 25 23:41:03 2024 -0500

    Update src/confighttp.cpp

    Co-authored-by: ReenigneArcher <[email protected]>

commit dd6b597
Author: Xander Frangos <[email protected]>
Date:   Thu Jan 25 23:18:51 2024 -0500

    Update src_assets/common/assets/web/troubleshooting.html

    Co-authored-by: ReenigneArcher <[email protected]>

commit 36b8bab
Author: Xander Frangos <[email protected]>
Date:   Thu Jan 25 23:18:38 2024 -0500

    Update src_assets/common/assets/web/pin.html

    Co-authored-by: ReenigneArcher <[email protected]>

commit 3f4fc1c
Author: Xander Frangos <[email protected]>
Date:   Thu Jan 25 21:36:25 2024 -0500

    Move Paired Client UI to Troubleshooting page

commit 0e1c02b
Author: Xander Frangos <[email protected]>
Date:   Thu Jan 25 21:20:11 2024 -0500

    Update API to use uuid instead of uniqueid for clients

commit 5cc7fc0
Author: Xander Frangos <[email protected]>
Date:   Wed Jan 24 17:17:10 2024 -0500

    Rename named_certs to named_devices

commit 541e341
Author: Xander Frangos <[email protected]>
Date:   Wed Jan 24 17:11:26 2024 -0500

    Refactor map_id_client into a single client_t

commit d842e97
Author: Xander Frangos <[email protected]>
Date:   Wed Jan 24 15:46:39 2024 -0500

    Formatting

commit 6c63992
Author: Xander Frangos <[email protected]>
Date:   Wed Jan 24 15:37:47 2024 -0500

    Formatting

commit b6010a6
Author: Xander Frangos <[email protected]>
Date:   Wed Jan 24 15:33:44 2024 -0500

    Update sunshine_state.json format to no longer use root.devices

    sunshine_state.json now follows the format outlined in #2042 (comment)

commit 8c8dfcf
Author: Xander Frangos <[email protected]>
Date:   Wed Jan 24 15:23:40 2024 -0500

    Fixed "restart Sunshine" banner not hiding after clicking "apply"

commit ced0122
Merge: 67988be 2511bd0
Author: Xander Frangos <[email protected]>
Date:   Sat Jan 20 04:55:46 2024 -0500

    Merge branch 'nightly' into unpair-single-client

commit 2511bd0
Merge: b8f8b46 a10ec3a
Author: Xander Frangos <[email protected]>
Date:   Sat Jan 20 04:54:59 2024 -0500

    Merge branch 'LizardByte:nightly' into nightly

commit 67988be
Author: Xander Frangos <[email protected]>
Date:   Sat Jan 20 04:43:14 2024 -0500

    Use lowercase property name for consistency

commit bb3fd9a
Author: Xander Frangos <[email protected]>
Date:   Sat Jan 20 04:16:48 2024 -0500

    Comment typo

commit 73bda36
Author: Xander Frangos <[email protected]>
Date:   Sat Jan 20 04:05:45 2024 -0500

    Formatting

commit b8f8b46
Author: Xander Frangos <[email protected]>
Date:   Sat Jan 20 02:59:37 2024 -0500

    Added UI for unpairing individual clients

commit 2e7655b
Author: Xander Frangos <[email protected]>
Date:   Sat Jan 20 02:58:15 2024 -0500

    During unpairing, also remove cert from client.certs

commit c8d74f2
Author: Xander Frangos <[email protected]>
Date:   Fri Jan 19 21:59:35 2024 -0500

    API to unpair clients

commit 4b9f9d0
Author: Xander Frangos <[email protected]>
Date:   Fri Jan 19 21:57:41 2024 -0500

    API to list paired clients

commit 223fdfa
Author: Xander Frangos <[email protected]>
Date:   Fri Jan 19 12:17:55 2024 -0500

    Generate and track uniqueID for named certs

commit 567725d
Author: Xander Frangos <[email protected]>
Date:   Fri Jan 19 12:17:22 2024 -0500

    Only read "certs" and "named_certs" if available

commit ca6d1a2
Author: Xander Frangos <[email protected]>
Date:   Fri Jan 19 12:16:25 2024 -0500

    Don't prepare nodes for old format

commit 9f6a485
Author: Xander Frangos <[email protected]>
Date:   Fri Jan 19 09:41:09 2024 -0500

    Assign name to certs when confirming PIN
xanderfrangos authored and ReenigneArcher committed May 27, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit d30a2ea5b0c8b25c7ea4d08608d07dd5777d9940
61 changes: 59 additions & 2 deletions src/confighttp.cpp
Original file line number Diff line number Diff line change
@@ -693,7 +693,8 @@ namespace confighttp {
// TODO: Input Validation
pt::read_json(ss, inputTree);
std::string pin = inputTree.get<std::string>("pin");
outputTree.put("status", nvhttp::pin(pin));
std::string name = inputTree.get<std::string>("name");
outputTree.put("status", nvhttp::pin(pin, name));
}
catch (std::exception &e) {
BOOST_LOG(warning) << "SavePin: "sv << e.what();
@@ -717,6 +718,60 @@ namespace confighttp {
response->write(data.str());
});
nvhttp::erase_all_clients();
proc::proc.terminate();
outputTree.put("status", true);
}

void
unpair(resp_https_t response, req_https_t request) {
if (!authenticate(response, request)) return;

print_req(request);

std::stringstream ss;
ss << request->content.rdbuf();

pt::ptree inputTree, outputTree;

auto g = util::fail_guard([&]() {
std::ostringstream data;
pt::write_json(data, outputTree);
response->write(data.str());
});

try {
// TODO: Input Validation
pt::read_json(ss, inputTree);
std::string uuid = inputTree.get<std::string>("uuid");
outputTree.put("status", nvhttp::unpair_client(uuid));
}
catch (std::exception &e) {
BOOST_LOG(warning) << "Unpair: "sv << e.what();
outputTree.put("status", false);
outputTree.put("error", e.what());
return;
}
}

void
listClients(resp_https_t response, req_https_t request) {
if (!authenticate(response, request)) return;

print_req(request);

pt::ptree named_certs = nvhttp::get_all_clients();

pt::ptree outputTree;

outputTree.put("status", false);

auto g = util::fail_guard([&]() {
std::ostringstream data;
pt::write_json(data, outputTree);
response->write(data.str());
});

outputTree.add_child("named_certs", named_certs);
outputTree.put("status", true);
}

@@ -765,7 +820,9 @@ namespace confighttp {
server.resource["^/api/restart$"]["POST"] = restart;
server.resource["^/api/password$"]["POST"] = savePassword;
server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp;
server.resource["^/api/clients/unpair$"]["POST"] = unpairAll;
server.resource["^/api/clients/unpair-all$"]["POST"] = unpairAll;
server.resource["^/api/clients/list$"]["GET"] = listClients;
server.resource["^/api/clients/unpair$"]["POST"] = unpair;
server.resource["^/api/apps/close$"]["POST"] = closeApp;
server.resource["^/api/covers/upload$"]["POST"] = uploadCover;
server.resource["^/images/sunshine.ico$"]["GET"] = getFaviconImage;
176 changes: 132 additions & 44 deletions src/nvhttp.cpp
Original file line number Diff line number Diff line change
@@ -117,9 +117,15 @@ namespace nvhttp {
std::string pkey;
} conf_intern;

struct named_cert_t {
std::string name;
std::string uuid;
std::string cert;
};

struct client_t {
std::string uniqueID;
std::vector<std::string> certs;
std::vector<named_cert_t> named_devices;
};

struct pair_session_t {
@@ -145,7 +151,7 @@ namespace nvhttp {

// uniqueID, session
std::unordered_map<std::string, pair_session_t> map_id_sess;
std::unordered_map<std::string, client_t> map_id_client;
client_t client_root;
std::atomic<uint32_t> session_id_counter;

using args_t = SimpleWeb::CaseInsensitiveMultimap;
@@ -189,22 +195,18 @@ namespace nvhttp {
root.erase("root"s);

root.put("root.uniqueid", http::unique_id);
auto &nodes = root.add_child("root.devices", pt::ptree {});
for (auto &[_, client] : map_id_client) {
pt::ptree node;

node.put("uniqueid"s, client.uniqueID);

pt::ptree cert_nodes;
for (auto &cert : client.certs) {
pt::ptree cert_node;
cert_node.put_value(cert);
cert_nodes.push_back(std::make_pair(""s, cert_node));
}
node.add_child("certs"s, cert_nodes);

nodes.push_back(std::make_pair(""s, node));
client_t &client = client_root;
pt::ptree node;

pt::ptree named_cert_nodes;
for (auto &named_cert : client.named_devices) {
pt::ptree named_cert_node;
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_nodes.push_back(std::make_pair(""s, named_cert_node));
}
root.add_child("root.named_devices"s, named_cert_nodes);

try {
pt::write_json(config::nvhttp.file_state, root);
@@ -223,48 +225,79 @@ namespace nvhttp {
return;
}

pt::ptree root;
pt::ptree tree;
try {
pt::read_json(config::nvhttp.file_state, root);
pt::read_json(config::nvhttp.file_state, tree);
}
catch (std::exception &e) {
BOOST_LOG(error) << "Couldn't read "sv << config::nvhttp.file_state << ": "sv << e.what();

return;
}

auto unique_id_p = root.get_optional<std::string>("root.uniqueid");
auto unique_id_p = tree.get_optional<std::string>("root.uniqueid");
if (!unique_id_p) {
// This file doesn't contain moonlight credentials
http::unique_id = uuid_util::uuid_t::generate().string();
return;
}
http::unique_id = std::move(*unique_id_p);

auto device_nodes = root.get_child("root.devices");

for (auto &[_, device_node] : device_nodes) {
auto uniqID = device_node.get<std::string>("uniqueid");
auto &client = map_id_client.emplace(uniqID, client_t {}).first->second;

client.uniqueID = uniqID;
auto root = tree.get_child("root");
client_t client;

// Import from old format
if (root.get_child_optional("devices")) {
auto device_nodes = root.get_child("devices");
for (auto &[_, device_node] : device_nodes) {
auto uniqID = device_node.get<std::string>("uniqueid");

if (device_node.count("certs")) {
for (auto &[_, el] : device_node.get_child("certs")) {
named_cert_t named_cert;
named_cert.name = ""s;
named_cert.cert = el.get_value<std::string>();
named_cert.uuid = uuid_util::uuid_t::generate().string();
client.named_devices.emplace_back(named_cert);
client.certs.emplace_back(named_cert.cert);
}
}
}
}

for (auto &[_, el] : device_node.get_child("certs")) {
client.certs.emplace_back(el.get_value<std::string>());
if (root.count("named_devices")) {
for (auto &[_, el] : root.get_child("named_devices")) {
named_cert_t named_cert;
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>();
client.named_devices.emplace_back(named_cert);
client.certs.emplace_back(named_cert.cert);
}
}

// Empty certificate chain and import certs from file
cert_chain.clear();
for (auto &cert : client.certs) {
cert_chain.add(crypto::x509(cert));
}
for (auto &named_cert : client.named_devices) {
cert_chain.add(crypto::x509(named_cert.cert));
}

client_root = client;
}

void
update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) {
switch (op) {
case op_e::ADD: {
auto &client = map_id_client[uniqueID];
client_t &client = client_root;
client.certs.emplace_back(std::move(cert));
client.uniqueID = uniqueID;
} break;
case op_e::REMOVE:
map_id_client.erase(uniqueID);
client_t client;
client_root = client;
break;
}

@@ -579,15 +612,16 @@ namespace nvhttp {
/**
* @brief Compare the user supplied pin to the Moonlight pin.
* @param pin The user supplied pin.
* @param name The user supplied name.
* @return `true` if the pin is correct, `false` otherwise.
*
* EXAMPLES:
* ```cpp
* bool pin_status = nvhttp::pin("1234");
* bool pin_status = nvhttp::pin("1234", "laptop");
* ```
*/
bool
pin(std::string pin) {
pin(std::string pin, std::string name) {
pt::ptree tree;
if (map_id_sess.empty()) {
return false;
@@ -613,6 +647,14 @@ namespace nvhttp {
auto &sess = std::begin(map_id_sess)->second;
getservercert(sess, tree, pin);

// set up named cert
client_t &client = client_root;
named_cert_t named_cert;
named_cert.name = name;
named_cert.cert = sess.client.cert;
named_cert.uuid = uuid_util::uuid_t::generate().string();
client.named_devices.emplace_back(named_cert);

// response to the request for pin
std::ostringstream data;
pt::write_xml(data, tree);
@@ -645,9 +687,7 @@ namespace nvhttp {
auto clientID = args.find("uniqueid"s);

if (clientID != std::end(args)) {
if (auto it = map_id_client.find(clientID->second); it != std::end(map_id_client)) {
pair_status = 1;
}
pair_status = 1;
}
}

@@ -742,6 +782,20 @@ namespace nvhttp {
response->close_connection_after_response = true;
}

pt::ptree
get_all_clients() {
pt::ptree named_cert_nodes;
client_t &client = client_root;
for (auto &named_cert : client.named_devices) {
pt::ptree named_cert_node;
named_cert_node.put("name"s, named_cert.name);
named_cert_node.put("uuid"s, named_cert.uuid);
named_cert_nodes.push_back(std::make_pair(""s, named_cert_node));
}

return named_cert_nodes;
}

void
applist(resp_https_t response, req_https_t request) {
print_req<SimpleWeb::HTTPS>(request);
@@ -1020,12 +1074,6 @@ namespace nvhttp {
conf_intern.pkey = file_handler::read_file(config::nvhttp.pkey.c_str());
conf_intern.servercert = file_handler::read_file(config::nvhttp.cert.c_str());

for (auto &[_, client] : map_id_client) {
for (auto &cert : client.certs) {
cert_chain.add(crypto::x509(cert));
}
}

auto add_cert = std::make_shared<safe::queue_t<crypto::x509_t>>(30);

// resume doesn't always get the parameter "localAudioPlayMode"
@@ -1149,8 +1197,48 @@ namespace nvhttp {
*/
void
erase_all_clients() {
map_id_client.clear();
client_t client;
client_root = client;
cert_chain.clear();
save_state();
}

/**
* @brief Remove single client.
*
* EXAMPLES:
* ```cpp
* nvhttp::unpair_client("4D7BB2DD-5704-A405-B41C-891A022932E1");
* ```
*/
int
unpair_client(std::string uuid) {
int removed = 0;
client_t &client = client_root;
for (auto it = client.named_devices.begin(); it != client.named_devices.end();) {
if ((*it).uuid == uuid) {
// Find matching cert and remove it
for (auto cert = client.certs.begin(); cert != client.certs.end();) {
if ((*cert) == (*it).cert) {
cert = client.certs.erase(cert);
removed++;
}
else {
++cert;
}
}

// And then remove the named cert
it = client.named_devices.erase(it);
removed++;
}
else {
++it;
}
}

save_state();
load_state();
return removed;
}
} // namespace nvhttp
9 changes: 8 additions & 1 deletion src/nvhttp.h
Original file line number Diff line number Diff line change
@@ -9,6 +9,9 @@
// standard includes
#include <string>

// lib includes
#include <boost/property_tree/ptree.hpp>

// local includes
#include "thread_safe.h"

@@ -43,7 +46,11 @@ namespace nvhttp {
void
start();
bool
pin(std::string pin);
pin(std::string pin, std::string name);
int
unpair_client(std::string uniqueid);
boost::property_tree::ptree
get_all_clients();
void
erase_all_clients();
} // namespace nvhttp
Loading