[WebUI] Added Basic Authentication (#1618)

* [WebUI] Added Basic Authentication

* Make the security option a MACRO to avoid typos in future updates
This commit is contained in:
Northern Man authored and GitHub committed 2023-04-27 16:49:09 -05:00
1 parent abeef870c7
commit acc0d91ca8
5 files changed
+148 -29

No files matched your search

+2 -1
View File
@@ -90,7 +90,8 @@ module.exports = {
'use/actuators',
'use/boards',
'use/displays',
'use/gateway'
'use/gateway',
'use/webui'
]
},
{
+49
View File
@@ -0,0 +1,49 @@
# WebUI
For ESP32 based environments a WebUI is available to provide basic configuration and monitoring of your OpenMQTTGateway Device. Functions included are:
* Configuration
* Information
* Firmware Upgrade
* Console
* Resart
# Login Authentication
By default access to the WebUI uses basic authentication to control access to your OpenMQTTGateway Device. The login is `admin` and the password is your ota_password.
# Configuration Options
## Wifi
Abiltity to change the SSID and password for your Wifi, if the change is unsuccessful it will revert back to the previous wifi settings.
## MQTT
Abiltity to change the mqtt settings, if the change is unsuccessful it will revert back to the previous mqtt settings.
## WebUI
Ability to change the display of sensor to Metric or Imperial, and disable the WebUI Authentication
## Logging
Ability to temporarily change the logging level.
# Information
Details of OpenMQTTGateway Device status
# Firmware Upgrade
Ability to upgrade firmware by URL or to latest version.
# Console
Ability to view messages from the OpenMQTTGateway console. The scope of messages visible in the UI is limited to just the OpenMQTTGatewy codebase, messages from the ESP hardware or other libraries are not visible,
Ability to inject commands to OpenMQTTGateway for processing. The commands accepted are of the form mqtt topic then json message. And as you are already on the target device, you do not need to include the device name ie
`commands/MQTTtoSYS/config {"cmd":"restart"}`
This works for all modules in your environment.
+78 -24
View File
@@ -45,22 +45,6 @@ QueueHandle_t webUIQueue;
WebServer server(80);
boolean displayMetric = DISPLAY_METRIC;
/*------------------- Local functions ----------------------*/
void notFound();
void handleRoot(); // "/"
void handleCS(); // Console
void handleCN(); // Configuration
void handleIN(); // Information
void handleRT(); // Restart
void handleCL(); // Configure Cloud
void handleTK(); // Return Cloud token
/*------------------- External functions ----------------------*/
esp_err_t nvs_flash_erase(void);
@@ -81,7 +65,11 @@ const uint16_t TOPSZ = 151; // Max number of characters in topic string
uint8_t masterlog_level; // Master log level used to override set log level
bool reset_web_log_flag = false; // Reset web console log
# ifdef ESP32
const char* www_username = WEBUI_LOGIN;
String authFailResponse = "Authentication Failed";
bool webUISecure = WEBUI_AUTH;
boolean displayMetric = DISPLAY_METRIC;
/*********************************************************************************************\
* ESP32 AutoMutex
\*********************************************************************************************/
@@ -241,8 +229,6 @@ String HtmlEscape(const String unescaped) {
return result;
}
# endif // ESP32
void AddLogData(uint32_t loglevel, const char* log_data, const char* log_data_payload = nullptr, const char* log_data_retained = nullptr) {
// Store log_data in buffer
// To lower heap usage log_data_payload may contain the payload data from MqttPublishPayload()
@@ -414,6 +400,7 @@ bool exists(String path) {
*/
void handleRoot() {
WEBUI_TRACE_LOG(F("handleRoot: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
@@ -476,6 +463,7 @@ void handleRoot() {
*
*/
void handleCN() {
WEBUI_SECURE
WEBUI_TRACE_LOG(F("handleCN: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
@@ -499,6 +487,54 @@ void handleCN() {
}
}
/**
* @brief /WU - Configuration Page
* T: handleWU: uri: /wu, args: 3, method: 1
* T: handleWU Arg: 0, dm=on - displayMetric
* T: handleWU Arg: 1, sw=on - webUISecure
* T: handleWU Arg: 2, save=
*/
void handleWU() {
WEBUI_TRACE_LOG(F("handleWU: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("handleWU Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
}
bool update = false;
if (displayMetric != server.hasArg("dm")) {
update = true;
}
displayMetric = server.hasArg("dm");
if (webUISecure != server.hasArg("sw")) {
update = true;
}
webUISecure = server.hasArg("sw");
if (server.hasArg("save") && update) {
WebUIConfig_save();
}
}
char jsonChar[100];
serializeJson(modules, jsonChar, measureJson(modules) + 1);
char buffer[WEB_TEMPLATE_BUFFER_MAX_SIZE];
snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, header_html, (String(gateway_name) + " - Configure WebUI").c_str());
String response = String(buffer);
response += String(script);
response += String(style);
int logLevel = Log.getLevel();
snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_webui_body, jsonChar, gateway_name, (displayMetric ? "checked" : ""), (webUISecure ? "checked" : ""));
response += String(buffer);
snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, footer, OMG_VERSION);
response += String(buffer);
server.send(200, "text/html", response);
}
/**
* @brief /WI - Configure WiFi Page
* T: handleWI: uri: /wi, args: 4, method: 1
@@ -508,6 +544,7 @@ void handleCN() {
*/
void handleWI() {
WEBUI_TRACE_LOG(F("handleWI: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
String WiFiScan = "";
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
@@ -670,6 +707,7 @@ void handleWI() {
*/
void handleMQ() {
WEBUI_TRACE_LOG(F("handleMQ: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("handleMQ Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
@@ -708,10 +746,10 @@ void handleMQ() {
}
// SC - Secure Connection argument is only present when true
WEBtoSYS["mqtt_secure"] = server.hasArg("sc");
if (mqtt_secure != server.hasArg("sc")) {
update = true;
}
WEBtoSYS["mqtt_secure"] = server.hasArg("sc");
if (!update) {
Log.warning(F("[WebUI] clearing" CR));
@@ -792,6 +830,7 @@ void handleMQ() {
*/
void handleLO() {
WEBUI_TRACE_LOG(F("handleLO: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("handleLO Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
@@ -825,6 +864,7 @@ void handleLO() {
*/
void handleRT() {
WEBUI_TRACE_LOG(F("handleRT: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("handleRT Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
@@ -862,6 +902,7 @@ void handleRT() {
*/
void handleCL() {
WEBUI_TRACE_LOG(F("handleCL: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("handleCL Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
@@ -915,6 +956,7 @@ void handleCL() {
*/
void handleTK() {
WEBUI_TRACE_LOG(F("handleTK: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("handleTK Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
@@ -957,6 +999,7 @@ void handleTK() {
*/
void handleIN() {
WEBUI_TRACE_LOG(F("handleCN: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("handleIN Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
@@ -1026,6 +1069,7 @@ void handleIN() {
*/
void handleUP() {
WEBUI_TRACE_LOG(F("handleUP: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("handleUP Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
@@ -1110,6 +1154,7 @@ void sendRestartPage() {
*/
void handleCS() {
WEBUI_TRACE_LOG(F("handleCS: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args() && server.hasArg("c2")) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("handleCS Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
@@ -1173,6 +1218,7 @@ void handleCS() {
*
*/
void notFound() {
WEBUI_SECURE
# ifdef WEBUI_DEVELOPMENT
String path = server.uri();
if (!exists(path)) {
@@ -1196,6 +1242,7 @@ void notFound() {
void WebUISetup() {
WEBUI_TRACE_LOG(F("ZwebUI setup start" CR));
WebUIConfig_load();
webUIQueue = xQueueCreate(5, sizeof(webUIQueueMessage*));
# ifdef WEBUI_DEVELOPMENT
@@ -1222,6 +1269,7 @@ void WebUISetup() {
server.on("/cn", handleCN); // Configuration
server.on("/wi", handleWI); // Configure Wifi
server.on("/mq", handleMQ); // Configure MQTT
server.on("/wu", handleWU); // Configure WebUI
# if defined(ZgatewayCloud)
server.on("/cl", handleCL); // Configure Cloud
server.on("/tk", handleTK); // Store Device Token
@@ -1233,6 +1281,8 @@ void WebUISetup() {
Log.begin(LOG_LEVEL, &WebLog);
Log.trace(F("[WebUI] displayMetric %T" CR), displayMetric);
Log.trace(F("[WebUI] WebUI Secure %T" CR), webUISecure);
Log.notice(F("OpenMQTTGateway URL: http://%s/" CR), WiFi.localIP().toString().c_str());
displayPrint("URL: http://", (char*)WiFi.localIP().toString().c_str());
Log.notice(F("ZwebUI setup done" CR));
@@ -1306,6 +1356,7 @@ String stateWebUIStatus() {
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
JsonObject WebUIdata = jsonBuffer.to<JsonObject>();
WebUIdata["displayMetric"] = (bool)displayMetric;
WebUIdata["webUISecure"] = (bool)webUISecure;
WebUIdata["displayQueue"] = uxQueueMessagesWaiting(webUIQueue);
;
@@ -1321,25 +1372,27 @@ bool WebUIConfig_save() {
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
JsonObject jo = jsonBuffer.to<JsonObject>();
jo["displayMetric"] = (bool)displayMetric;
jo["webUISecure"] = (bool)webUISecure;
// Save config into NVS (non-volatile storage)
String conf = "";
serializeJson(jsonBuffer, conf);
preferences.begin(Gateway_Short_Name, false);
preferences.putString("WebUIConfig", conf);
int result = preferences.putString("WebUIConfig", conf);
preferences.end();
Log.trace(F("[WebUI] WebUIConfig_save: %s, result: %d" CR), conf.c_str(), result);
return true;
}
void WebUIConfig_init() {
boolean displayMetric = DISPLAY_METRIC;
displayMetric = DISPLAY_METRIC;
webUISecure = WEBUI_AUTH;
Log.notice(F("WebUI config initialised" CR));
}
bool WebUIConfig_load() {
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
preferences.begin(Gateway_Short_Name, true);
String exists = preferences.getString("WebUIConfig", "{}");
if (exists != "{}") {
if (preferences.isKey("WebUIConfig")) {
auto error = deserializeJson(jsonBuffer, preferences.getString("WebUIConfig", "{}"));
preferences.end();
if (error) {
@@ -1352,6 +1405,7 @@ bool WebUIConfig_load() {
}
JsonObject jo = jsonBuffer.as<JsonObject>();
displayMetric = jo["displayMetric"].as<bool>();
webUISecure = jo["webUISecure"].as<bool>();
return true;
} else {
preferences.end();
+4 -4
View File
@@ -45,8 +45,8 @@
#else
# define configure_3
#endif
#define configure_4 "<p><form action='lo' method='get'><button>Configure Logging</button></form></p>"
#define configure_5
#define configure_4 "<p><form action='wu' method='get'><button>Configure WebUI</button></form></p>"
#define configure_5 "<p><form action='lo' method='get'><button>Configure Logging</button></form></p>"
#define configure_6
#define configure_7
#define configure_8
@@ -83,8 +83,6 @@ const char console_body[] = body_header "<br><textarea readonly id='t1' cols='34
const char information_body[] = body_header "<style>td {padding: 0px 5px;}</style><div id='i' name='i'></div>" body_footer_main_menu;
// <fieldset class=\"set1\"><legend><span><b>&nbsp;Upgrade by file upload&nbsp;</b></span></legend><form method='post' action='up' enctype='multipart/form-data'><br><input type='file' name='u2'><br><br><button type='submit' onclick='eb(\"f1\").style.display=\"none\";eb(\"f2\").style.display=\"block\";this.form.submit();'>Start upgrade</button></form></fieldset>
const char upgrade_body[] = body_header "<div id='f1' style='display:block;'><fieldset class=\"set1\"><legend><span><b>Upgrade by Web Server</b></span></legend><form method='get' action='up'><br><b>OTA URL</b><br><input id='o' placeholder=\"OTA_URL\" value=\"%s\"><br><br><button type='submit' class='button bgrn'>Start upgrade</button></form></fieldset><br><br><fieldset class=\"set1\"><legend><span><b>Upgrade to Level</b></span></legend><form method='get' action='up'><p><b>Level</b><br><select id='le'><option value='1'>Latest Release</option><option value='2'>Development</option></select></p><br><button type='submit' class='button bgrn'>Start upgrade</button></form></fieldset></div><div id='f2' style='display:none;text-align:center;'><b>Upload started ...</b></div><div id=but2d style=\"display: block;\"></div><p>" body_footer_main_menu;
const char config_wifi_body[] = body_header "%s<br><div><a href='/wi?scan='><b>Scan for all WiFi Networks</b></a></div><br><fieldset class=\"set1\"><legend><span><b>WiFi Parameters</b></span></legend><form method='get' action='wi'><p><b>WiFi Network</b> () <br><input id='s1' placeholder=\"Type or Select your WiFi Network\" value=\"%s\"></p><p><label><b>WiFi Password</b><input type='checkbox' onclick='sp(\"p1\")'></label><br><input id='p1' type='password' placeholder=\"Enter your WiFi Password\" value=\"%s\"></p><br><button name='save' type='submit' class='button bgrn'>Save</button></form></fieldset>" body_footer_config_menu;
@@ -95,6 +93,8 @@ const char config_mqtt_body[] = body_header "<fieldset class=\"set1\"><legend><s
const char config_logging_body[] = body_header "<fieldset class=\"set1\"><legend><span><b>OpenMQTTGateway Logging</b></span></legend><form method='get' action='lo'><p><b>Log Level</b><br><select id='lo'><option %s value='0'>Silent</option><option %s value='1'>Fatal</option><option %s value='2'>Error</option><option %s value='3'>Warning</option><option %s value='4'>Notice</option><option %s value='5'>Trace</option><option %s value='6'>Verbose</option></select></p><br><button name='save' type='submit' class='button bgrn'>Save</button></form></fieldset>" body_footer_config_menu;
const char config_webui_body[] = body_header "<fieldset class=\"set1\"><legend><span><b>Configure WebUI</b></span></legend><form method='get' action='wu'><p><b>Display Metric</b><br><input id='dm' type='checkbox' %s></p><p><b>Secure WebUI</b><br><input id='sw' type='checkbox' %s></p><br><button name='save' type='submit' class='button bgrn'>Save</button></form></fieldset>" body_footer_config_menu;
const char footer[] = "<div style='text-align:right;font-size:11px;'><hr/><a href='https://community.openmqttgateway.com' target='_blank' style='color:#aaa;'>%s</a></div></div></body></html>";
#endif
+15
View File
@@ -57,8 +57,23 @@
# define WEBUI_TRACE_LOG(...)
#endif
#ifndef WEBUI_AUTH
# define WEBUI_AUTH true // Default to WebUI authentication
#endif
#ifndef WEBUI_LOGIN
# define WEBUI_LOGIN "admin"
#endif
/*------------------- End of Compiler Directives ----------------------*/
#define WEBUI_SECURE \
if (webUISecure) { \
if (!server.authenticate(www_username, ota_pass)) { \
return server.requestAuthentication(DIGEST_AUTH, gateway_name, authFailResponse); \
} \
}
#define MAX_WIFI_NETWORKS_TO_SHOW 10
/*