Added support for ip whitelist and added rest method to update/get the config file

This commit is contained in:
Leon Etienne (ubuntu wsl) 2020-10-02 13:16:57 +02:00
parent 66882b3b67
commit 5f7e4e2cf2
9 changed files with 198 additions and 23 deletions

View File

@ -82,6 +82,7 @@ void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
http_message* hpm = (http_message*)p;
std::string requestedUri = FixUnterminatedString(hpm->uri.p, hpm->uri.len);
// Get clients ip address
std::string peer_addr;
{
char buf[32];
@ -89,13 +90,7 @@ void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
peer_addr = buf;
}
if ((XGConfig::general.onlyAllowLocalhost) && (peer_addr != "127.0.0.1"))
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(UNAUTHORIZED, "Only localhost allowed!"));
ServeStringToConnection(pNc, j.Render(), UNAUTHORIZED);
}
else
if (IsConnectionAllowed(peer_addr))
{
try
{
@ -128,6 +123,12 @@ void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
break;
}
else // Client is not allowed, serve error json
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(UNAUTHORIZED, "Only localhost allowed!"));
ServeStringToConnection(pNc, j.Render(), UNAUTHORIZED);
}
}
return;
@ -199,6 +200,36 @@ void HttpServer::ServeDownloadeableResource(mg_connection* pNc, int ev, void* p,
return;
}
bool HttpServer::IsConnectionAllowed(std::string peer_address)
{
// Localhost is always allowed!
if (peer_address == "127.0.0.1") return true;
// Peer is not localhost, but only localhost is allowed!
else if (XGConfig::access.only_allow_localhost) return false;
// Let's check if the whitelist is active
else if (XGConfig::access.enable_whitelist)
{
// It is. Let's check if our peer is whitelisted
for (std::size_t i = 0; i < XGConfig::access.whitelist.size(); i++)
{
// Whitelist is enabled, and peer is whitelisted
if (XGConfig::access.whitelist[i] == peer_address) return true;
}
// Whitelist is enabled, but peer is NOT whitelisted
return false;
}
else // Whitelist is NOT enabled and only_allow_localhost is FALSE!
{
return true;
}
// Should never come to this point
return false;
}
void HttpServer::OnExit()
{
log->cout << "Shutting down http-server...";

View File

@ -29,6 +29,8 @@ namespace Rest
static void ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode = 200);
static std::string FixUnterminatedString(const char* cstr, const std::size_t len);
static bool IsConnectionAllowed(std::string peer_address);
struct mg_mgr* pMgr;
struct mg_connection* pNc;

View File

@ -42,6 +42,7 @@ bool RestQueryHandler::ProcessQuery(const std::string clientAdress, const Json&
else if (requestName == "fetch_alltime_logs") return FetchAlltimeLogs(requestBody, responseBody, responseCode);
else if (requestName == "update_dep_youtubedl") return UpdateYoutubeDL(requestBody, responseBody, responseCode);
else if (requestName == "remove_download_entry") return RemoveDownloadEntry(requestBody, responseBody, responseCode);
else if (requestName == "update_config") return UpdateConfig(requestBody, responseBody, responseCode);
@ -437,6 +438,44 @@ bool RestQueryHandler::RemoveDownloadEntry(const JsonBlock& request, JsonBlock&
return true;
}
bool RestQueryHandler::UpdateConfig(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
{
if (ValidateField("config", JDType::JSON, request, responseBody))
{
bool prevStateConsole = XGConfig::general.show_console;
XGConfig::LoadFromJson(request.Get("config").AsJson);
// Update console, if necessary
if (XGConfig::general.show_console != prevStateConsole)
{
if (XGConfig::general.show_console) ConsoleManager::ShowConsole();
else ConsoleManager::HideConsole();
}
log->cout << "Updated config values...";
log->Flush();
XGConfig::Save();
responseBody.Set("message") = "Updated no settings";
}
else
{
responseBody.Set("message") = "Updated no settings";
}
responseCode = OK;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(OK));
responseBody.Set("settings") = XGConfig::GetSavefileBuffer();
return true;
}
bool RestQueryHandler::ValidateField(const std::string name, const JasonPP::JDType type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere)

View File

@ -34,6 +34,7 @@ namespace Rest
static bool GetDiskUsage(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool UpdateYoutubeDL(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool RemoveDownloadEntry(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool UpdateConfig(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool ValidateField(const std::string name, const JasonPP::JDType type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere);

View File

@ -45,13 +45,20 @@ void XGConfig::InitializeDefaultValues()
downloader.num_threads = 1;
general.show_console = true;
general.onlyAllowLocalhost = true;
access.only_allow_localhost = true;
access.enable_whitelist = true;
access.whitelist = std::vector<std::string>();
access.whitelist.push_back("127.0.0.1"); // Add localhost to whitelist
return;
}
void XGConfig::LoadFromJson(const JasonPP::JsonBlock& json)
{
savefileBuffer.Clear();
savefileBuffer.CloneFrom(json);
if (IsJsonFieldValid(json, "logging.logfile_json", JDType::STRING))
{
logging.logfile_json = json.ShorthandGet("logging.logfile_json").AsString;
@ -103,9 +110,28 @@ void XGConfig::LoadFromJson(const JasonPP::JsonBlock& json)
{
general.show_console = json.ShorthandGet("general.show_console").AsBool;
}
if (IsJsonFieldValid(json, "general.onlyAllowLocalhost", JDType::BOOL))
if (IsJsonFieldValid(json, "access.only_allow_localhost", JDType::BOOL))
{
general.onlyAllowLocalhost = json.ShorthandGet("general.onlyAllowLocalhost").AsBool;
access.only_allow_localhost = json.ShorthandGet("access.only_allow_localhost").AsBool;
}
if (IsJsonFieldValid(json, "access.enable_whitelist", JDType::BOOL))
{
access.enable_whitelist = json.ShorthandGet("access.enable_whitelist").AsBool;
}
if (IsJsonFieldValid(json, "access.whitelist", JDType::ARRAY))
{
const JsonArray& cachedArr = json.ShorthandGet("access.whitelist").AsArray;
access.whitelist.clear();
for (std::size_t i = 0; i < cachedArr.Size(); i++)
{
if (cachedArr[i].GetDataType() == JDType::STRING)
{
access.whitelist.push_back(cachedArr[i].AsString);
}
}
}
return;
@ -113,10 +139,13 @@ void XGConfig::LoadFromJson(const JasonPP::JsonBlock& json)
JsonBlock XGConfig::CreateJson()
{
JsonArray jsnArrWhitelist;
jsnArrWhitelist.Add(access.whitelist);
return JsonBlock({
Ele("httpServer", JsonBlock({
Ele("port", httpServer.port),
Ele("pollingRate", httpServer.polling_rate),
Ele("polling_rate", httpServer.polling_rate),
Ele("rootdir", httpServer.rootdir)
})),
Ele("logging", JsonBlock({
@ -131,7 +160,11 @@ JsonBlock XGConfig::CreateJson()
})),
Ele("general", JsonBlock({
Ele("show_console", general.show_console),
Ele("onlyAllowLocalhost", general.onlyAllowLocalhost)
})),
Ele("access", JsonBlock({
Ele("only_allow_localhost", access.only_allow_localhost),
Ele("enable_whitelist", access.enable_whitelist),
Ele("whitelist", jsnArrWhitelist),
}))
});
}
@ -210,6 +243,8 @@ void XGConfig::Save()
bool XGConfig::SaveToFile(std::string filename)
{
Json cfgStruct(CreateJson());
savefileBuffer.Clear();
savefileBuffer.CloneFrom(cfgStruct.AsJson);
return FileSystem::WriteFile(filename, cfgStruct.Render());
}
@ -245,5 +280,7 @@ XGConfig::HttpServer XGConfig::httpServer;
XGConfig::Logging XGConfig::logging;
XGConfig::Downloader XGConfig::downloader;
XGConfig::General XGConfig::general;
XGConfig::Access XGConfig::access;
JasonPP::JsonBlock XGConfig::savefileBuffer;
::Logging::Logger* XGConfig::log;

View File

@ -1,4 +1,5 @@
#pragma once
#include <vector>
#include "Filesystem.h"
#include "external_dependencies/leonetienne/JasonPP/JasonPP.hpp"
#include "Logger.h"
@ -30,27 +31,37 @@ public:
struct General
{
bool show_console;
bool onlyAllowLocalhost;
};
struct Access
{
bool only_allow_localhost;
bool enable_whitelist;
std::vector<std::string> whitelist;
};
static void PreInit();
static void Save();
static void PostExit();
static void LoadFromJson(const JasonPP::JsonBlock& json);
static XGConfig::HttpServer httpServer;
static XGConfig::Logging logging;
static XGConfig::Downloader downloader;
static XGConfig::General general;
static XGConfig::Access access;
static const JasonPP::JsonBlock& GetSavefileBuffer() { return savefileBuffer; }
static ::Logging::Logger* log;
private:
static bool SaveToFile(std::string filename);
static JasonPP::JsonBlock savefileBuffer;
static bool IsJsonFieldValid(const JasonPP::JsonBlock& json, const std::string key, const JasonPP::JDType type);
static void InitializeDefaultValues();
static JasonPP::JsonBlock CreateJson();
static void LoadFromJson(const JasonPP::JsonBlock& json);
static void Load();
};

View File

@ -39,7 +39,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"request\": \"queue_download\",\r\n \"video_url\": \"https://youtu.be/OF-thWTJcu0\",\r\n \"mode\": \"video\"\r\n}",
"raw": "{\r\n \"request\": \"queue_download\",\r\n \"video_url\": \"https://www.youtube.com/watch?v=BHiWygziyso\",\r\n \"mode\": \"video\"\r\n}",
"options": {
"raw": {
"language": "json"
@ -66,7 +66,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"request\": \"queue_download\",\r\n \"video_url\": \"https://youtu.be/wgfNsek8xkc\",\r\n \"mode\": \"audio\"\r\n}",
"raw": "{\r\n \"request\": \"queue_download\",\r\n \"video_url\": \"https://youtu.be/m9HT74QccbQ\",\r\n \"mode\": \"audio\"\r\n}",
"options": {
"raw": {
"language": "json"
@ -93,7 +93,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"request\": \"fetch_session_cache\",\r\n \"max-num\": -1\r\n}\r\n",
"raw": "{\r\n \"request\": \"fetch_session_cache\",\r\n \"max_age\": -1\r\n}\r\n",
"options": {
"raw": {
"language": "json"
@ -426,7 +426,61 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"request\": \"remove_download_entry\",\r\n \"id\": \"1KnnPV\"\r\n}",
"raw": "{\r\n \"request\": \"remove_download_entry\",\r\n \"id\": \"1KnnMe\"\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "localhost:6969/api",
"host": [
"localhost"
],
"port": "6969",
"path": [
"api"
]
}
},
"response": []
},
{
"name": "UpdateConfig",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"request\": \"update_config\",\r\n \"config\": {\r\n \"access\": {\r\n \"enable_whitelist\": false,\r\n \"only_allow_localhost\": false,\r\n \"whitelist\": [\r\n \"127.0.0.1\"\r\n ]\r\n },\r\n \"general\": {\r\n \"show_console\": true\r\n }\r\n }\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "localhost:6969/api",
"host": [
"localhost"
],
"port": "6969",
"path": [
"api"
]
}
},
"response": []
},
{
"name": "GetConfig",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"request\": \"update_config\"\r\n}",
"options": {
"raw": {
"language": "json"

View File

@ -14,11 +14,6 @@
<Toggle :isOn="false"/>
</div>
<div class="option toggle flex justify-between items-center">
<p>Limit speed</p>
<Toggle :isOn="false"/>
</div>
<div class="option text narrow flex justify-between w-full items-center">
<p class="mr-3">Max speed</p>
<input type="text" id="max_speed" name="max_speed" placeholder="100M">
@ -29,6 +24,11 @@
<input type="text" id="max_threads" name="max_threads" placeholder="10">
</div>
<div class="option toggle flex justify-between items-center">
<p>Enable whitelist</p>
<Toggle :isOn="false"/>
</div>
</div>
<div class="w-full lg:w-1/2 border-left lg:pl-3 mt-6 lg:mt-0">