diff --git a/assets/web/config.html b/assets/web/config.html index fa9612d9..b2b642cb 100644 --- a/assets/web/config.html +++ b/assets/web/config.html @@ -1,5 +1,5 @@
-

Configuration

+

Configuration

\ No newline at end of file diff --git a/assets/web/password.html b/assets/web/password.html new file mode 100644 index 00000000..3e3f2140 --- /dev/null +++ b/assets/web/password.html @@ -0,0 +1,97 @@ +
+

Password Change

+
+
+
+

Current Credentials

+
+ + +
 
+
+
+ + +
+
+
+

New Credentials

+
+ + +
If not specified, the username will not change +
+
+
+ + +
+
+ + +
+
+
+
Error: {{error}}
+
Success! This page will reload soon, your browser will ask you for the new credentials
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/sunshine/confighttp.cpp b/sunshine/confighttp.cpp index fb20a8dd..e76b8ca4 100644 --- a/sunshine/confighttp.cpp +++ b/sunshine/confighttp.cpp @@ -135,6 +135,14 @@ void getConfigPage(std::shared_ptr::Response> response->write(header + content); } +template +void getPasswordPage(std::shared_ptr::Response> response, std::shared_ptr::Request> request) { + if(!authenticate(response, request)) return; + std::string header = read_file(WEB_DIR "header.html"); + std::string content = read_file(WEB_DIR "password.html"); + response->write(header + content); +} + 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); @@ -306,6 +314,54 @@ 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; + std::stringstream ss; + std::stringstream configStream; + ss << request->content.rdbuf(); + + pt::ptree inputTree,outputTree,fileTree; + + 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 username = inputTree.get("currentUsername"); + std::string newUsername = inputTree.get("newUsername"); + std::string password = inputTree.get("currentPassword"); + std::string newPassword = inputTree.get("newPassword"); + std::string confirmPassword = inputTree.get("confirmNewPassword"); + if(newUsername.length() == 0) newUsername = username; + std::string hash = crypto::hash_hexstr(password + config::sunshine.salt); + 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",crypto::hash_hexstr(newPassword + config::sunshine.salt)); + fileTree.put("salt",config::sunshine.salt); + pt::write_json(config::sunshine.credentials_file,fileTree); + http::reload_user_creds(config::sunshine.credentials_file); + outputTree.put("status",true); + } else { + outputTree.put("status",false); + outputTree.put("error","Invalid Current Credentials"); + } + } + catch(std::exception &e) { + BOOST_LOG(warning) << e.what(); + outputTree.put("status", false); + outputTree.put("error", e.what()); + return; + } +} + void start(std::shared_ptr shutdown_event) { auto ctx = std::make_shared(boost::asio::ssl::context::tls); ctx->use_certificate_chain_file(config::nvhttp.cert); @@ -315,14 +371,16 @@ void start(std::shared_ptr shutdown_event) { http_server.resource["^/$"]["GET"] = getIndexPage; http_server.resource["^/pin$"]["GET"] = getPinPage; http_server.resource["^/apps$"]["GET"] = getAppsPage; + http_server.resource["^/clients$"]["GET"] = getClientsPage; + http_server.resource["^/config$"]["GET"] = getConfigPage; + http_server.resource["^/password$"]["GET"] = getPasswordPage; + http_server.resource["^/pin/([0-9]+)$"]["GET"] = nvhttp::pin; http_server.resource["^/api/apps$"]["GET"] = getApps; http_server.resource["^/api/apps$"]["POST"] = saveApp; http_server.resource["^/api/config$"]["GET"] = getConfig; http_server.resource["^/api/config$"]["POST"] = saveConfig; + http_server.resource["^/api/password$"]["POST"] = savePassword; http_server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp; - http_server.resource["^/clients$"]["GET"] = getClientsPage; - http_server.resource["^/config$"]["GET"] = getConfigPage; - http_server.resource["^/pin/([0-9]+)$"]["GET"] = nvhttp::pin; http_server.config.reuse_address = true; http_server.config.address = "0.0.0.0"s; http_server.config.port = PORT_HTTP; diff --git a/sunshine/httpcommon.h b/sunshine/httpcommon.h index 4997b2b1..7c441a7c 100644 --- a/sunshine/httpcommon.h +++ b/sunshine/httpcommon.h @@ -4,6 +4,7 @@ void init(std::shared_ptr shutdown_event); int create_creds(const std::string &pkey, const std::string &cert); std::string read_file(const char *path); int write_file(const char *path, const std::string_view &contents); +int reload_user_creds(const std::string &file); extern std::string unique_id; extern net::net_e origin_pin_allowed; } // namespace http \ No newline at end of file