mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-02-11 15:40:11 +00:00
fix adding and editing apps json
This commit is contained in:
parent
cf77b301bc
commit
17d3fcf0d0
@ -37,6 +37,10 @@
|
||||
# The file where current state of Sunshine is stored
|
||||
# file_state = sunshine_state.json
|
||||
|
||||
# The file where user credentials for the UI are stored
|
||||
# By default, credentials are stored in `file_state`
|
||||
# credentials_file = sunshine_state.json
|
||||
|
||||
# The display modes advertised by Sunshine
|
||||
#
|
||||
# Some versions of Moonlight, such as Moonlight-nx (Switch),
|
||||
@ -228,7 +232,7 @@
|
||||
|
||||
#################################### VAAPI ###################################
|
||||
####### adapter ##########
|
||||
# Unlike with `amfvce` and `nvenc`, it doesn't matter if video encoding is done
|
||||
# Unlike with `amdvce` and `nvenc`, it doesn't matter if video encoding is done
|
||||
# on a different GPU.
|
||||
# Run the following commands:
|
||||
# 1. ls /dev/dri/renderD*
|
||||
|
@ -15,7 +15,8 @@
|
||||
<tr v-for="(app,i) in apps" :key="i">
|
||||
<td>{{app.name}}</td>
|
||||
<td><button class="btn btn-primary" @click="editApp(i)">Edit</button>
|
||||
<button class="btn btn-danger" @click="showDeleteForm(i)">Delete</button></td>
|
||||
<button class="btn btn-danger" @click="showDeleteForm(i)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -41,7 +42,7 @@
|
||||
<label for="appName" class="form-label">Command Preparations</label>
|
||||
<div class="form-text">A list of commands to be run before/after the application. <br> If any of the
|
||||
prep-commands fail, starting the application is aborted</div>
|
||||
<table v-if="editForm['prep-cmd'].length > 0">
|
||||
<table v-if="editForm['prep-cmd'].length > 0">
|
||||
<thead>
|
||||
<th class="precmd-head">Do</th>
|
||||
<th class="precmd-head">Undo</th>
|
||||
@ -73,7 +74,8 @@
|
||||
<!--command-->
|
||||
<div class="mb-3">
|
||||
<label for="appCmd" class="form-label">Command</label>
|
||||
<input type="text" class="form-control monospace" id="appCmd" aria-describedby="appCmdHelp" v-model="editForm.cmd">
|
||||
<input type="text" class="form-control monospace" id="appCmd" aria-describedby="appCmdHelp"
|
||||
v-model="editForm.cmd">
|
||||
<div id="appCmdHelp" class="form-text">The main application, if it is not specified, a processs is started that
|
||||
sleeps indefinitely</div>
|
||||
</div>
|
||||
@ -84,7 +86,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2" v-else >
|
||||
<div class="mt-2" v-else>
|
||||
<button class="btn btn-primary" @click="newApp">+ Add New</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -113,7 +115,8 @@
|
||||
output: '',
|
||||
cmd: [],
|
||||
index: -1,
|
||||
"prep-cmd": []
|
||||
"prep-cmd": [],
|
||||
"detached": []
|
||||
};
|
||||
this.editForm.index = -1;
|
||||
this.showEditForm = true;
|
||||
@ -156,5 +159,4 @@
|
||||
.monospace {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
</style>
|
@ -7,6 +7,7 @@
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "main.h"
|
||||
#include "utility.h"
|
||||
|
||||
#define CA_DIR "credentials"
|
||||
@ -209,12 +210,12 @@ input_t input {
|
||||
};
|
||||
|
||||
sunshine_t sunshine {
|
||||
2, // min_log_level
|
||||
0, // flags
|
||||
"user_credentials.json"s, //User file
|
||||
""s, //Username
|
||||
""s, //Password
|
||||
""s //Password Salt
|
||||
2, // min_log_level
|
||||
0, // flags
|
||||
{}, //User file
|
||||
{}, //Username
|
||||
{}, //Password
|
||||
{} //Password Salt
|
||||
};
|
||||
|
||||
bool endline(char ch) {
|
||||
@ -297,7 +298,7 @@ parse_option(std::string_view::const_iterator begin, std::string_view::const_ite
|
||||
std::make_pair(to_string(begin, end_name), to_string(begin_val, endl)));
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> parse_config(std::string_view file_content) {
|
||||
std::unordered_map<std::string, std::string> parse_config(const std::string_view &file_content) {
|
||||
std::unordered_map<std::string, std::string> vars;
|
||||
|
||||
auto pos = std::begin(file_content);
|
||||
@ -567,6 +568,11 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
|
||||
list_string_f(vars, "resolutions"s, nvhttp.resolutions);
|
||||
list_int_f(vars, "fps"s, nvhttp.fps);
|
||||
|
||||
string_f(vars, "credentials_file", config::sunshine.credentials_file);
|
||||
if(config::sunshine.credentials_file.empty()) {
|
||||
config::sunshine.credentials_file = config::nvhttp.file_state;
|
||||
}
|
||||
|
||||
string_f(vars, "audio_sink", audio.sink);
|
||||
string_f(vars, "virtual_sink", audio.virtual_sink);
|
||||
|
||||
@ -681,18 +687,9 @@ int parse(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
std::ifstream in { config_file };
|
||||
sunshine.config_file = config_file;
|
||||
|
||||
if(!in.is_open()) {
|
||||
std::cout << "Error: Couldn't open "sv << config_file << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto vars = parse_config(std::string {
|
||||
// Quick and dirty
|
||||
std::istreambuf_iterator<char>(in),
|
||||
std::istreambuf_iterator<char>() });
|
||||
auto vars = parse_config(read_file(config_file));
|
||||
|
||||
for(auto &[name, value] : cmd_vars) {
|
||||
vars.insert_or_assign(std::move(name), std::move(value));
|
||||
|
@ -82,7 +82,7 @@ namespace flag {
|
||||
enum flag_e : std::size_t {
|
||||
PIN_STDIN = 0, // Read PIN from stdin instead of http
|
||||
FRESH_STATE, // Do not load or save state
|
||||
CONST_PIN = 4, // Use "universal" pin
|
||||
CONST_PIN, // Use "universal" pin
|
||||
FLAG_SIZE
|
||||
};
|
||||
}
|
||||
@ -94,6 +94,8 @@ struct sunshine_t {
|
||||
std::string username;
|
||||
std::string password;
|
||||
std::string salt;
|
||||
|
||||
std::string config_file;
|
||||
};
|
||||
|
||||
extern video_t video;
|
||||
@ -104,6 +106,6 @@ extern input_t input;
|
||||
extern sunshine_t sunshine;
|
||||
|
||||
int parse(int argc, char *argv[]);
|
||||
std::unordered_map<std::string, std::string> parse_config(std::string_view file_content);
|
||||
std::unordered_map<std::string, std::string> parse_config(const std::string_view &file_content);
|
||||
} // namespace config
|
||||
#endif
|
||||
|
@ -30,8 +30,6 @@
|
||||
#include "utility.h"
|
||||
#include "uuid.h"
|
||||
|
||||
std::string read_file(std::string path);
|
||||
|
||||
namespace confighttp {
|
||||
using namespace std::literals;
|
||||
constexpr auto PORT_HTTPS = 47990;
|
||||
@ -50,6 +48,23 @@ enum class op_e {
|
||||
REMOVE
|
||||
};
|
||||
|
||||
void print_req(const req_https_t &request) {
|
||||
BOOST_LOG(debug) << "METHOD :: "sv << request->method;
|
||||
BOOST_LOG(debug) << "DESTINATION :: "sv << request->path;
|
||||
|
||||
for(auto &[name, val] : request->header) {
|
||||
BOOST_LOG(debug) << name << " -- " << val;
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << " [--] "sv;
|
||||
|
||||
for(auto &[name, val] : request->parse_query_string()) {
|
||||
BOOST_LOG(debug) << name << " -- " << val;
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << " [--] "sv;
|
||||
}
|
||||
|
||||
void send_unauthorized(resp_https_t response, req_https_t request) {
|
||||
auto address = request->remote_endpoint_address();
|
||||
BOOST_LOG(info) << '[' << address << "] -- denied"sv;
|
||||
@ -62,27 +77,40 @@ void send_unauthorized(resp_https_t response, req_https_t request) {
|
||||
bool authenticate(resp_https_t response, req_https_t request) {
|
||||
auto address = request->remote_endpoint_address();
|
||||
auto ip_type = net::from_address(address);
|
||||
|
||||
if(ip_type > http::origin_pin_allowed) {
|
||||
BOOST_LOG(info) << '[' << address << "] -- denied"sv;
|
||||
response->write(SimpleWeb::StatusCode::client_error_forbidden);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fg = util::fail_guard([&]() {
|
||||
send_unauthorized(response, request);
|
||||
});
|
||||
|
||||
auto auth = request->header.find("authorization");
|
||||
if(auth == request->header.end()) {
|
||||
send_unauthorized(response, request);
|
||||
return false;
|
||||
}
|
||||
std::string rawAuth = auth->second;
|
||||
std::string authData = rawAuth.substr("Basic "sv.length());
|
||||
authData = SimpleWeb::Crypto::Base64::decode(authData);
|
||||
int index = authData.find(':');
|
||||
std::string username = authData.substr(0, index);
|
||||
std::string password = authData.substr(index + 1);
|
||||
std::string hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
|
||||
if(username == config::sunshine.username && hash == config::sunshine.password) return true;
|
||||
|
||||
send_unauthorized(response, request);
|
||||
return false;
|
||||
auto &rawAuth = auth->second;
|
||||
auto authData = SimpleWeb::Crypto::Base64::decode(rawAuth.substr("Basic "sv.length()));
|
||||
|
||||
int index = authData.find(':');
|
||||
if(index >= authData.size() - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto username = authData.substr(0, index);
|
||||
auto password = authData.substr(index + 1);
|
||||
auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
|
||||
|
||||
if(username != config::sunshine.username || hash != config::sunshine.password) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fg.disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
void not_found(resp_https_t response, req_https_t request) {
|
||||
@ -101,6 +129,8 @@ void not_found(resp_https_t response, req_https_t request) {
|
||||
void getIndexPage(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "index.html");
|
||||
response->write(header + content);
|
||||
@ -109,6 +139,8 @@ void getIndexPage(resp_https_t response, req_https_t request) {
|
||||
void getPinPage(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "pin.html");
|
||||
response->write(header + content);
|
||||
@ -117,6 +149,8 @@ void getPinPage(resp_https_t response, req_https_t request) {
|
||||
void getAppsPage(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "apps.html");
|
||||
response->write(header + content);
|
||||
@ -125,6 +159,8 @@ void getAppsPage(resp_https_t response, req_https_t request) {
|
||||
void getClientsPage(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "clients.html");
|
||||
response->write(header + content);
|
||||
@ -133,6 +169,8 @@ void getClientsPage(resp_https_t response, req_https_t request) {
|
||||
void getConfigPage(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "config.html");
|
||||
response->write(header + content);
|
||||
@ -141,6 +179,8 @@ void getConfigPage(resp_https_t response, req_https_t request) {
|
||||
void getPasswordPage(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "password.html");
|
||||
response->write(header + content);
|
||||
@ -149,15 +189,20 @@ void getPasswordPage(resp_https_t response, req_https_t request) {
|
||||
void getApps(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
std::string content = read_file(SUNSHINE_ASSETS_DIR "/" APPS_JSON);
|
||||
print_req(request);
|
||||
|
||||
std::string content = read_file(config::stream.file_apps.c_str());
|
||||
response->write(content);
|
||||
}
|
||||
|
||||
void saveApp(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 outputTree;
|
||||
auto g = util::fail_guard([&]() {
|
||||
std::ostringstream data;
|
||||
@ -165,20 +210,28 @@ void saveApp(resp_https_t response, req_https_t request) {
|
||||
pt::write_json(data, outputTree);
|
||||
response->write(data.str());
|
||||
});
|
||||
|
||||
pt::ptree inputTree, fileTree;
|
||||
|
||||
BOOST_LOG(fatal) << config::stream.file_apps;
|
||||
try {
|
||||
//TODO: Input Validation
|
||||
pt::read_json(ss, inputTree);
|
||||
pt::read_json(SUNSHINE_ASSETS_DIR "/" APPS_JSON, fileTree);
|
||||
pt::read_json(config::stream.file_apps, fileTree);
|
||||
|
||||
if(inputTree.get_child("prep-cmd").empty()) {
|
||||
inputTree.erase("prep-cmd");
|
||||
}
|
||||
|
||||
if(inputTree.get_child("detached").empty()) {
|
||||
inputTree.erase("detached");
|
||||
}
|
||||
|
||||
auto &apps_node = fileTree.get_child("apps"s);
|
||||
int index = inputTree.get<int>("index");
|
||||
if(inputTree.get_child("prep-cmd").empty())
|
||||
inputTree.erase("prep-cmd");
|
||||
|
||||
if(inputTree.get_child("detached").empty())
|
||||
inputTree.erase("detached");
|
||||
|
||||
inputTree.erase("index");
|
||||
|
||||
if(index == -1) {
|
||||
apps_node.push_back(std::make_pair("", inputTree));
|
||||
}
|
||||
@ -198,21 +251,25 @@ void saveApp(resp_https_t response, req_https_t request) {
|
||||
fileTree.erase("apps");
|
||||
fileTree.push_back(std::make_pair("apps", newApps));
|
||||
}
|
||||
pt::write_json(SUNSHINE_ASSETS_DIR "/" APPS_JSON, fileTree);
|
||||
outputTree.put("status", "true");
|
||||
proc::refresh(SUNSHINE_ASSETS_DIR "/" APPS_JSON);
|
||||
pt::write_json(config::stream.file_apps, fileTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(warning) << e.what();
|
||||
BOOST_LOG(warning) << "SaveApp: "sv << e.what();
|
||||
|
||||
outputTree.put("status", "false");
|
||||
outputTree.put("error", "Invalid Input JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
outputTree.put("status", "true");
|
||||
proc::refresh(config::stream.file_apps);
|
||||
}
|
||||
|
||||
void deleteApp(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
pt::ptree outputTree;
|
||||
auto g = util::fail_guard([&]() {
|
||||
std::ostringstream data;
|
||||
@ -225,8 +282,8 @@ void deleteApp(resp_https_t response, req_https_t request) {
|
||||
pt::read_json(config::stream.file_apps, fileTree);
|
||||
auto &apps_node = fileTree.get_child("apps"s);
|
||||
int index = stoi(request->path_match[1]);
|
||||
BOOST_LOG(info) << index;
|
||||
if(index <= 0) {
|
||||
|
||||
if(index < 0) {
|
||||
outputTree.put("status", "false");
|
||||
outputTree.put("error", "Invalid Index");
|
||||
return;
|
||||
@ -236,29 +293,31 @@ void deleteApp(resp_https_t response, req_https_t request) {
|
||||
pt::ptree newApps;
|
||||
int i = 0;
|
||||
for(const auto &kv : apps_node) {
|
||||
if(i != index) {
|
||||
if(i++ != index) {
|
||||
newApps.push_back(std::make_pair("", kv.second));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
fileTree.erase("apps");
|
||||
fileTree.push_back(std::make_pair("apps", newApps));
|
||||
}
|
||||
pt::write_json(SUNSHINE_ASSETS_DIR "/" APPS_JSON, fileTree);
|
||||
outputTree.put("status", "true");
|
||||
proc::refresh(SUNSHINE_ASSETS_DIR "/" APPS_JSON);
|
||||
pt::write_json(config::stream.file_apps, fileTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(warning) << e.what();
|
||||
BOOST_LOG(warning) << "DeleteApp: "sv << e.what();
|
||||
outputTree.put("status", "false");
|
||||
outputTree.put("error", "Invalid File JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
outputTree.put("status", "true");
|
||||
proc::refresh(config::stream.file_apps);
|
||||
}
|
||||
|
||||
void getConfig(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
pt::ptree outputTree;
|
||||
auto g = util::fail_guard([&]() {
|
||||
std::ostringstream data;
|
||||
@ -266,36 +325,22 @@ void getConfig(resp_https_t response, req_https_t request) {
|
||||
pt::write_json(data, outputTree);
|
||||
response->write(data.str());
|
||||
});
|
||||
try {
|
||||
outputTree.put("status", "true");
|
||||
outputTree.put("platform", SUNSHINE_PLATFORM);
|
||||
const char *config_file = SUNSHINE_ASSETS_DIR "/sunshine.conf";
|
||||
std::ifstream in { config_file };
|
||||
|
||||
if(!in.is_open()) {
|
||||
std::cout << "Error: Couldn't open "sv << config_file << std::endl;
|
||||
}
|
||||
outputTree.put("status", "true");
|
||||
outputTree.put("platform", SUNSHINE_PLATFORM);
|
||||
|
||||
auto vars = config::parse_config(std::string {
|
||||
// Quick and dirty
|
||||
std::istreambuf_iterator<char>(in),
|
||||
std::istreambuf_iterator<char>() });
|
||||
auto vars = config::parse_config(read_file(config::sunshine.config_file.c_str()));
|
||||
|
||||
for(auto &[name, value] : vars) {
|
||||
outputTree.put(std::move(name), std::move(value));
|
||||
}
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(warning) << e.what();
|
||||
outputTree.put("status", "false");
|
||||
outputTree.put("error", "Invalid File JSON");
|
||||
return;
|
||||
for(auto &[name, value] : vars) {
|
||||
outputTree.put(std::move(name), std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
void saveConfig(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::stringstream ss;
|
||||
std::stringstream configStream;
|
||||
ss << request->content.rdbuf();
|
||||
@ -316,10 +361,10 @@ void saveConfig(resp_https_t response, req_https_t request) {
|
||||
|
||||
configStream << kv.first << " = " << value << std::endl;
|
||||
}
|
||||
write_file(SUNSHINE_ASSETS_DIR "/sunshine.conf", configStream.str());
|
||||
write_file(config::sunshine.config_file.c_str(), configStream.str());
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(warning) << e.what();
|
||||
BOOST_LOG(warning) << "SaveConfig: "sv << e.what();
|
||||
outputTree.put("status", "false");
|
||||
outputTree.put("error", e.what());
|
||||
return;
|
||||
@ -329,11 +374,13 @@ void saveConfig(resp_https_t response, req_https_t request) {
|
||||
void savePassword(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::stringstream ss;
|
||||
std::stringstream configStream;
|
||||
ss << request->content.rdbuf();
|
||||
|
||||
pt::ptree inputTree, outputTree, fileTree;
|
||||
pt::ptree inputTree, outputTree;
|
||||
|
||||
auto g = util::fail_guard([&]() {
|
||||
std::ostringstream data;
|
||||
@ -344,23 +391,21 @@ void savePassword(resp_https_t response, req_https_t request) {
|
||||
try {
|
||||
//TODO: Input Validation
|
||||
pt::read_json(ss, inputTree);
|
||||
std::string username = inputTree.get<std::string>("currentUsername");
|
||||
std::string newUsername = inputTree.get<std::string>("newUsername");
|
||||
std::string password = inputTree.get<std::string>("currentPassword");
|
||||
std::string newPassword = inputTree.get<std::string>("newPassword");
|
||||
std::string confirmPassword = inputTree.get<std::string>("confirmNewPassword");
|
||||
auto username = inputTree.get<std::string>("currentUsername");
|
||||
auto newUsername = inputTree.get<std::string>("newUsername");
|
||||
auto password = inputTree.get<std::string>("currentPassword");
|
||||
auto newPassword = inputTree.get<std::string>("newPassword");
|
||||
auto confirmPassword = inputTree.get<std::string>("confirmNewPassword");
|
||||
if(newUsername.length() == 0) newUsername = username;
|
||||
|
||||
std::string hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
|
||||
auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
|
||||
if(username == config::sunshine.username && hash == config::sunshine.password) {
|
||||
if(newPassword != confirmPassword) {
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", "Password Mismatch");
|
||||
}
|
||||
fileTree.put("username", newUsername);
|
||||
fileTree.put("password", util::hex(crypto::hash(newPassword + config::sunshine.salt)).to_string());
|
||||
fileTree.put("salt", config::sunshine.salt);
|
||||
pt::write_json(config::sunshine.credentials_file, fileTree);
|
||||
|
||||
http::save_user_creds(config::sunshine.credentials_file, newUsername, newPassword, crypto::rand_alphabet(16));
|
||||
http::reload_user_creds(config::sunshine.credentials_file);
|
||||
outputTree.put("status", true);
|
||||
}
|
||||
@ -370,7 +415,7 @@ void savePassword(resp_https_t response, req_https_t request) {
|
||||
}
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(warning) << e.what();
|
||||
BOOST_LOG(warning) << "SavePassword: "sv << e.what();
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", e.what());
|
||||
return;
|
||||
@ -380,6 +425,8 @@ void savePassword(resp_https_t response, req_https_t request) {
|
||||
void savePin(resp_https_t response, req_https_t request) {
|
||||
if(!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << request->content.rdbuf();
|
||||
|
||||
@ -398,7 +445,7 @@ void savePin(resp_https_t response, req_https_t request) {
|
||||
outputTree.put("status", nvhttp::pin(pin));
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(warning) << e.what();
|
||||
BOOST_LOG(warning) << "SavePin: "sv << e.what();
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", e.what());
|
||||
return;
|
||||
@ -462,19 +509,4 @@ void start(std::shared_ptr<safe::signal_t> shutdown_event) {
|
||||
|
||||
tcp.join();
|
||||
}
|
||||
} // namespace confighttp
|
||||
|
||||
std::string read_file(std::string path) {
|
||||
std::ifstream in(path);
|
||||
|
||||
std::string input;
|
||||
std::string base64_cert;
|
||||
|
||||
//FIXME: Being unable to read file could result in infinite loop
|
||||
while(!in.eof()) {
|
||||
std::getline(in, input);
|
||||
base64_cert += input + '\n';
|
||||
}
|
||||
|
||||
return base64_cert;
|
||||
}
|
||||
} // namespace confighttp
|
@ -30,9 +30,9 @@ using namespace std::literals;
|
||||
namespace fs = std::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
int create_creds(const std::string &pkey, const std::string &cert);
|
||||
int generate_user_creds(const std::string &file);
|
||||
int reload_user_creds(const std::string &file);
|
||||
bool user_creds_exist(const std::string &file);
|
||||
|
||||
std::string unique_id;
|
||||
net::net_e origin_pin_allowed;
|
||||
|
||||
@ -52,8 +52,8 @@ void init(std::shared_ptr<safe::signal_t> shutdown_event) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(!fs::exists(config::sunshine.credentials_file)) {
|
||||
if(generate_user_creds(config::sunshine.credentials_file)) {
|
||||
if(!user_creds_exist(config::sunshine.credentials_file)) {
|
||||
if(save_user_creds(config::sunshine.credentials_file, "sunshine"s, crypto::rand_alphabet(16), crypto::rand_alphabet(16))) {
|
||||
shutdown_event->raise(true);
|
||||
return;
|
||||
}
|
||||
@ -64,27 +64,56 @@ void init(std::shared_ptr<safe::signal_t> shutdown_event) {
|
||||
}
|
||||
}
|
||||
|
||||
int generate_user_creds(const std::string &file) {
|
||||
int save_user_creds(const std::string &file, const std::string &username, const std::string &password, const std::string &salt) {
|
||||
pt::ptree outputTree;
|
||||
|
||||
if(fs::exists(file)) {
|
||||
try {
|
||||
pt::read_json(file, outputTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(error) << "Couldn't read user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
outputTree.put("username", "sunshine");
|
||||
outputTree.put("salt", salt);
|
||||
outputTree.put("password", util::hex(crypto::hash(password + salt)).to_string());
|
||||
try {
|
||||
std::string username = "sunshine";
|
||||
std::string plainPassword = crypto::rand_alphabet(16);
|
||||
std::string salt = crypto::rand_alphabet(16);
|
||||
outputTree.put("username", "sunshine");
|
||||
outputTree.put("salt", salt);
|
||||
outputTree.put("password", util::hex(crypto::hash(plainPassword + salt)).to_string());
|
||||
BOOST_LOG(info) << "New credentials have been created";
|
||||
BOOST_LOG(info) << "Username: " << username;
|
||||
BOOST_LOG(info) << "Password: " << plainPassword;
|
||||
pt::write_json(file, outputTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(fatal) << e.what();
|
||||
return 1;
|
||||
BOOST_LOG(error) << "generating user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "New credentials have been created"sv;
|
||||
BOOST_LOG(info) << "Username: "sv << username;
|
||||
BOOST_LOG(info) << "Password: "sv << password;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool user_creds_exist(const std::string &file) {
|
||||
if(!fs::exists(file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pt::ptree inputTree;
|
||||
try {
|
||||
pt::read_json(file, inputTree);
|
||||
return inputTree.find("username") != inputTree.not_found() &&
|
||||
inputTree.find("password") != inputTree.not_found() &&
|
||||
inputTree.find("salt") != inputTree.not_found();
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(error) << "validating user credentials: "sv << e.what();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int reload_user_creds(const std::string &file) {
|
||||
pt::ptree inputTree;
|
||||
try {
|
||||
@ -94,8 +123,8 @@ int reload_user_creds(const std::string &file) {
|
||||
config::sunshine.salt = inputTree.get<std::string>("salt");
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(fatal) << e.what();
|
||||
return 1;
|
||||
BOOST_LOG(error) << "loading user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -114,23 +143,23 @@ int create_creds(const std::string &pkey, const std::string &cert) {
|
||||
std::error_code err_code {};
|
||||
fs::create_directories(pkey_dir, err_code);
|
||||
if(err_code) {
|
||||
BOOST_LOG(fatal) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message();
|
||||
BOOST_LOG(error) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs::create_directories(cert_dir, err_code);
|
||||
if(err_code) {
|
||||
BOOST_LOG(fatal) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message();
|
||||
BOOST_LOG(error) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(write_file(pkey.c_str(), creds.pkey)) {
|
||||
BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.pkey << ']';
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.pkey << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(write_file(cert.c_str(), creds.x509)) {
|
||||
BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.cert << ']';
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.cert << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -139,7 +168,7 @@ int create_creds(const std::string &pkey, const std::string &cert) {
|
||||
fs::perm_options::replace, err_code);
|
||||
|
||||
if(err_code) {
|
||||
BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message();
|
||||
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -148,7 +177,7 @@ int create_creds(const std::string &pkey, const std::string &cert) {
|
||||
fs::perm_options::replace, err_code);
|
||||
|
||||
if(err_code) {
|
||||
BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message();
|
||||
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ namespace http {
|
||||
|
||||
void init(std::shared_ptr<safe::signal_t> shutdown_event);
|
||||
int create_creds(const std::string &pkey, const std::string &cert);
|
||||
int save_user_creds(const std::string &file, const std::string &username, const std::string &password, const std::string &salt);
|
||||
int reload_user_creds(const std::string &file);
|
||||
extern std::string unique_id;
|
||||
extern net::net_e origin_pin_allowed;
|
||||
|
@ -93,6 +93,18 @@ enum class op_e {
|
||||
void save_state() {
|
||||
pt::ptree root;
|
||||
|
||||
if(fs::exists(config::nvhttp.file_state)) {
|
||||
try {
|
||||
pt::read_json(config::nvhttp.file_state, root);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(error) << e.what();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -111,7 +123,13 @@ void save_state() {
|
||||
nodes.push_back(std::make_pair(""s, node));
|
||||
}
|
||||
|
||||
pt::write_json(config::nvhttp.file_state, root);
|
||||
try {
|
||||
pt::write_json(config::nvhttp.file_state, root);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(error) << e.what();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void load_state() {
|
||||
@ -131,7 +149,16 @@ void load_state() {
|
||||
return;
|
||||
}
|
||||
|
||||
http::unique_id = root.get<std::string>("root.uniqueid");
|
||||
auto unique_id_p = root.find("root.uniqueid"s);
|
||||
if(unique_id_p == root.not_found()) {
|
||||
// This file doesn't contain moonlight credentials
|
||||
http::unique_id = util::uuid_t::generate().string();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
http::unique_id = root.get<std::string>("root.uniqueid");
|
||||
}
|
||||
|
||||
auto device_nodes = root.get_child("root.devices");
|
||||
|
||||
for(auto &[_, device_node] : device_nodes) {
|
||||
|
@ -5,15 +5,11 @@
|
||||
#ifndef SUNSHINE_NVHTTP_H
|
||||
#define SUNSHINE_NVHTTP_H
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include "thread_safe.h"
|
||||
#include <Simple-Web-Server/server_http.hpp>
|
||||
#include <Simple-Web-Server/server_https.hpp>
|
||||
#include "thread_safe.h"
|
||||
|
||||
#define CA_DIR SUNSHINE_ASSETS_DIR "/demoCA"
|
||||
#define PRIVATE_KEY_FILE CA_DIR "/cakey.pem"
|
||||
#define CERTIFICATE_FILE CA_DIR "/cacert.pem"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace nvhttp {
|
||||
void start(std::shared_ptr<safe::signal_t> shutdown_event);
|
||||
|
Loading…
x
Reference in New Issue
Block a user