mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
fix authentication check for GET commands that need admin - Refactor MQTT subscriptions and API calls #173
This commit is contained in:
@@ -30,7 +30,7 @@ std::vector<Command::CmdFunction> Command::cmdfunctions_;
|
|||||||
// the path is leading so if duplicate keys are in the input JSON it will be ignored
|
// the path is leading so if duplicate keys are in the input JSON it will be ignored
|
||||||
// the entry point will be either via the Web API (api/) or MQTT (<base>/)
|
// the entry point will be either via the Web API (api/) or MQTT (<base>/)
|
||||||
// returns a return code and json output
|
// returns a return code and json output
|
||||||
uint8_t Command::process(const char * path, const bool authenticated, const JsonObject & input, JsonObject & output) {
|
uint8_t Command::process(const char * path, const bool is_admin, const JsonObject & input, JsonObject & output) {
|
||||||
SUrlParser p; // parse URL for the path names
|
SUrlParser p; // parse URL for the path names
|
||||||
p.parse(path);
|
p.parse(path);
|
||||||
|
|
||||||
@@ -146,16 +146,16 @@ uint8_t Command::process(const char * path, const bool authenticated, const Json
|
|||||||
// call the command based on the type
|
// call the command based on the type
|
||||||
uint8_t return_code = CommandRet::ERROR;
|
uint8_t return_code = CommandRet::ERROR;
|
||||||
if (data.is<const char *>()) {
|
if (data.is<const char *>()) {
|
||||||
return_code = Command::call(device_type, command_p, data.as<const char *>(), authenticated, id_n, output);
|
return_code = Command::call(device_type, command_p, data.as<const char *>(), is_admin, id_n, output);
|
||||||
} else if (data.is<int>()) {
|
} else if (data.is<int>()) {
|
||||||
char data_str[10];
|
char data_str[10];
|
||||||
return_code = Command::call(device_type, command_p, Helpers::itoa(data_str, (int16_t)data.as<int>()), authenticated, id_n, output);
|
return_code = Command::call(device_type, command_p, Helpers::itoa(data_str, (int16_t)data.as<int>()), is_admin, id_n, output);
|
||||||
} else if (data.is<float>()) {
|
} else if (data.is<float>()) {
|
||||||
char data_str[10];
|
char data_str[10];
|
||||||
return_code = Command::call(device_type, command_p, Helpers::render_value(data_str, (float)data.as<float>(), 2), authenticated, id_n, output);
|
return_code = Command::call(device_type, command_p, Helpers::render_value(data_str, (float)data.as<float>(), 2), is_admin, id_n, output);
|
||||||
} else if (data.isNull()) {
|
} else if (data.isNull()) {
|
||||||
// empty
|
// empty
|
||||||
return_code = Command::call(device_type, command_p, "", authenticated, id_n, output);
|
return_code = Command::call(device_type, command_p, "", is_admin, id_n, output);
|
||||||
} else {
|
} else {
|
||||||
// can't process
|
// can't process
|
||||||
output.clear();
|
output.clear();
|
||||||
@@ -243,7 +243,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
|
|||||||
// calls a command. Takes a json object for output.
|
// calls a command. Takes a json object for output.
|
||||||
// id may be used to represent a heating circuit for example
|
// id may be used to represent a heating circuit for example
|
||||||
// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed
|
// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed
|
||||||
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id, JsonObject & output) {
|
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, const bool is_admin, const int8_t id, JsonObject & output) {
|
||||||
uint8_t return_code = CommandRet::OK;
|
uint8_t return_code = CommandRet::OK;
|
||||||
|
|
||||||
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
||||||
@@ -274,7 +274,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check permissions
|
// check permissions
|
||||||
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated) {
|
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !is_admin) {
|
||||||
output["message"] = "authentication failed";
|
output["message"] = "authentication failed";
|
||||||
return CommandRet::NOT_ALLOWED; // command not allowed
|
return CommandRet::NOT_ALLOWED; // command not allowed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,11 +49,11 @@ enum CommandFlag : uint8_t {
|
|||||||
|
|
||||||
// return status after calling a Command
|
// return status after calling a Command
|
||||||
enum CommandRet : uint8_t {
|
enum CommandRet : uint8_t {
|
||||||
FAIL = 0, // 0 or FALSE
|
FAIL = 0, // 0 or FALSE
|
||||||
OK, // 1 or TRUE
|
OK, // 1 or TRUE
|
||||||
NOT_FOUND, // 2
|
NOT_FOUND, // 2
|
||||||
ERROR, // 3
|
ERROR, // 3
|
||||||
NOT_ALLOWED // needs authentication
|
NOT_ALLOWED // needs authentication
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ class Command {
|
|||||||
|
|
||||||
#define add_
|
#define add_
|
||||||
|
|
||||||
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id, JsonObject & output);
|
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, const bool is_admin, const int8_t id, JsonObject & output);
|
||||||
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value);
|
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value);
|
||||||
|
|
||||||
// with normal call back function taking a value and id
|
// with normal call back function taking a value and id
|
||||||
@@ -130,7 +130,7 @@ class Command {
|
|||||||
|
|
||||||
static bool list(const uint8_t device_type, JsonObject & output);
|
static bool list(const uint8_t device_type, JsonObject & output);
|
||||||
|
|
||||||
static uint8_t process(const char * path, const bool authenticated, const JsonObject & input, JsonObject & output);
|
static uint8_t process(const char * path, const bool is_admin, const JsonObject & input, JsonObject & output);
|
||||||
|
|
||||||
static const char * parse_command_string(const char * command, int8_t & id);
|
static const char * parse_command_string(const char * command, int8_t & id);
|
||||||
|
|
||||||
|
|||||||
@@ -472,7 +472,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (command == "api") {
|
if (command == "api") {
|
||||||
shell.printfln(F("Testing API with MQTT and REST"));
|
shell.printfln(F("Testing API with MQTT and REST, standalone"));
|
||||||
|
|
||||||
|
|
||||||
Mqtt::ha_enabled(true);
|
Mqtt::ha_enabled(true);
|
||||||
// Mqtt::ha_enabled(false);
|
// Mqtt::ha_enabled(false);
|
||||||
@@ -483,17 +484,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
run_test("boiler");
|
run_test("boiler");
|
||||||
run_test("thermostat");
|
run_test("thermostat");
|
||||||
|
|
||||||
/*
|
#if defined(EMSESP_STANDALONE)
|
||||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/mode"); // empty payload, sends reponse
|
|
||||||
EMSESP::mqtt_.incoming("ems-esp/boiler/syspress"); // empty payload, sends reponse
|
|
||||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/mode", "auto"); // set mode
|
|
||||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/mode"); // empty payload, sends reponse
|
|
||||||
EMSESP::mqtt_.incoming("ems-esp/system/send", "11 12 13");
|
|
||||||
EMSESP::mqtt_.incoming("ems-esp/system/publish");
|
|
||||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/seltemp"); // empty payload, sends reponse
|
|
||||||
EMSESP::mqtt_.incoming("ems-esp/system/send", "11 12 13");
|
|
||||||
return;
|
|
||||||
*/
|
|
||||||
|
|
||||||
AsyncWebServerRequest request2;
|
AsyncWebServerRequest request2;
|
||||||
request2.method(HTTP_GET);
|
request2.method(HTTP_GET);
|
||||||
@@ -545,11 +536,20 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
request2.url("/api/system/restart");
|
||||||
|
EMSESP::webAPIService.webAPIService_get(&request2);
|
||||||
|
return;
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
request2.url("/api/dallassensor/fdfd");
|
request2.url("/api/dallassensor/fdfd");
|
||||||
EMSESP::webAPIService.webAPIService_get(&request2);
|
EMSESP::webAPIService.webAPIService_get(&request2);
|
||||||
emsesp::EMSESP::logger().notice("****");
|
emsesp::EMSESP::logger().notice("****");
|
||||||
request2.url("/api/dallassensor/info");
|
request2.url("/api/dallassensor/info");
|
||||||
EMSESP::webAPIService.webAPIService_get(&request2);
|
EMSESP::webAPIService.webAPIService_get(&request2);
|
||||||
|
return;
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
AsyncWebServerRequest request2;
|
AsyncWebServerRequest request2;
|
||||||
@@ -603,6 +603,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
cmd = Command::parse_command_string(command_s, id_n);
|
cmd = Command::parse_command_string(command_s, id_n);
|
||||||
shell.printfln("test cmd parse cmd=%s id=%d", cmd, id_n);
|
shell.printfln("test cmd parse cmd=%s id=%d", cmd, id_n);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// Console tests
|
// Console tests
|
||||||
shell.invoke_command("call thermostat entities");
|
shell.invoke_command("call thermostat entities");
|
||||||
shell.invoke_command("call thermostat mode auto");
|
shell.invoke_command("call thermostat mode auto");
|
||||||
@@ -622,6 +624,19 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/modee", "auto"); // unknown command
|
EMSESP::mqtt_.incoming("ems-esp/thermostat/modee", "auto"); // unknown command
|
||||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/mode/auto", "auto"); // invalid
|
EMSESP::mqtt_.incoming("ems-esp/thermostat/mode/auto", "auto"); // invalid
|
||||||
|
|
||||||
|
// various MQTT checks
|
||||||
|
/*
|
||||||
|
EMSESP::mqtt_.incoming("ems-esp/thermostat/mode"); // empty payload, sends reponse
|
||||||
|
EMSESP::mqtt_.incoming("ems-esp/boiler/syspress"); // empty payload, sends reponse
|
||||||
|
EMSESP::mqtt_.incoming("ems-esp/thermostat/mode", "auto"); // set mode
|
||||||
|
EMSESP::mqtt_.incoming("ems-esp/thermostat/mode"); // empty payload, sends reponse
|
||||||
|
EMSESP::mqtt_.incoming("ems-esp/system/send", "11 12 13");
|
||||||
|
EMSESP::mqtt_.incoming("ems-esp/system/publish");
|
||||||
|
EMSESP::mqtt_.incoming("ems-esp/thermostat/seltemp"); // empty payload, sends reponse
|
||||||
|
EMSESP::mqtt_.incoming("ems-esp/system/send", "11 12 13");
|
||||||
|
return;
|
||||||
|
*/
|
||||||
|
|
||||||
// check long base
|
// check long base
|
||||||
Mqtt::base("home/cellar/heating");
|
Mqtt::base("home/cellar/heating");
|
||||||
EMSESP::mqtt_.incoming("home/cellar/heating/thermostat/mode"); // empty payload, sends reponse
|
EMSESP::mqtt_.incoming("home/cellar/heating/thermostat/mode"); // empty payload, sends reponse
|
||||||
|
|||||||
@@ -31,17 +31,17 @@ WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * security
|
|||||||
server->addHandler(&_apiHandler);
|
server->addHandler(&_apiHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP GET
|
||||||
// GET /{device}
|
// GET /{device}
|
||||||
// GET /{device}/{name}
|
// GET /{device}/{entity}
|
||||||
// GET /device={device}?cmd={name}?data={value}[?id={hc}]
|
|
||||||
void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) {
|
void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) {
|
||||||
// has no body JSON so create dummy as empty object
|
// has no body JSON so create dummy as empty input object
|
||||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> input_doc;
|
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> input_doc;
|
||||||
JsonObject input = input_doc.to<JsonObject>();
|
JsonObject input = input_doc.to<JsonObject>();
|
||||||
parse(request, input);
|
parse(request, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For POSTS with an optional JSON body
|
// For HTTP POSTS with an optional JSON body
|
||||||
// HTTP_POST | HTTP_PUT | HTTP_PATCH
|
// HTTP_POST | HTTP_PUT | HTTP_PATCH
|
||||||
// POST /{device}[/{hc|id}][/{name}]
|
// POST /{device}[/{hc|id}][/{name}]
|
||||||
void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json) {
|
void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||||
@@ -59,23 +59,21 @@ void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVari
|
|||||||
// parse the URL looking for query or path parameters
|
// parse the URL looking for query or path parameters
|
||||||
// reporting back any errors
|
// reporting back any errors
|
||||||
void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||||
auto method = request->method();
|
auto method = request->method();
|
||||||
bool authenticated = false;
|
|
||||||
|
|
||||||
// if its a POST then check authentication
|
// see if the user has admin privelegs
|
||||||
if (method != HTTP_GET) {
|
bool is_admin;
|
||||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||||
Authentication authentication = _securityManager->authenticateRequest(request);
|
Authentication authentication = _securityManager->authenticateRequest(request);
|
||||||
authenticated = settings.notoken_api | AuthenticationPredicates::IS_ADMIN(authentication);
|
is_admin = settings.notoken_api | AuthenticationPredicates::IS_ADMIN(authentication);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// output json buffer
|
// output json buffer
|
||||||
PrettyAsyncJsonResponse * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN);
|
PrettyAsyncJsonResponse * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||||
JsonObject output = response->getRoot();
|
JsonObject output = response->getRoot();
|
||||||
|
|
||||||
// call command
|
// call command
|
||||||
uint8_t return_code = Command::process(request->url().c_str(), authenticated, input, output);
|
uint8_t return_code = Command::process(request->url().c_str(), is_admin, input, output);
|
||||||
|
|
||||||
if (return_code != CommandRet::OK) {
|
if (return_code != CommandRet::OK) {
|
||||||
char error[100];
|
char error[100];
|
||||||
|
|||||||
Reference in New Issue
Block a user