Authentication added
This commit is contained in:
+57
-4
@@ -12,6 +12,7 @@
|
|||||||
#define PWM_FREQ 5000
|
#define PWM_FREQ 5000
|
||||||
#define PWM_RES 10
|
#define PWM_RES 10
|
||||||
#define MQTT_MANUFACTURER "Baumann Enkataleiptics"
|
#define MQTT_MANUFACTURER "Baumann Enkataleiptics"
|
||||||
|
#define AUTH_USERNAME "admin"
|
||||||
|
|
||||||
struct MeterConfig {
|
struct MeterConfig {
|
||||||
int pin;
|
int pin;
|
||||||
@@ -32,10 +33,16 @@ struct MqttConfig {
|
|||||||
char prefix[32] = "m1730";
|
char prefix[32] = "m1730";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AuthConfig {
|
||||||
|
bool enabled = false;
|
||||||
|
char pass[32] = "";
|
||||||
|
};
|
||||||
|
|
||||||
static char hostname[32] = HOSTNAME_DEFAULT;
|
static char hostname[32] = HOSTNAME_DEFAULT;
|
||||||
static int meterCount = 0;
|
static int meterCount = 0;
|
||||||
static MeterConfig meters[MAX_METERS] = {};
|
static MeterConfig meters[MAX_METERS] = {};
|
||||||
static MqttConfig mqttCfg;
|
static MqttConfig mqttCfg;
|
||||||
|
static AuthConfig authCfg;
|
||||||
static WiFiManagerParameter hostnameParam("hostname", "Device hostname", hostname, 32);
|
static WiFiManagerParameter hostnameParam("hostname", "Device hostname", hostname, 32);
|
||||||
static WebServer server(80);
|
static WebServer server(80);
|
||||||
static WiFiClient wifiClient;
|
static WiFiClient wifiClient;
|
||||||
@@ -70,6 +77,12 @@ static void loadConfig() {
|
|||||||
strlcpy(mqttCfg.prefix, mq["prefix"] | "m1730", sizeof(mqttCfg.prefix));
|
strlcpy(mqttCfg.prefix, mq["prefix"] | "m1730", sizeof(mqttCfg.prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonObject auth = doc["auth"];
|
||||||
|
if (!auth.isNull()) {
|
||||||
|
authCfg.enabled = auth["en"] | false;
|
||||||
|
strlcpy(authCfg.pass, auth["pass"] | "", sizeof(authCfg.pass));
|
||||||
|
}
|
||||||
|
|
||||||
JsonArray arr = doc["meters"].as<JsonArray>();
|
JsonArray arr = doc["meters"].as<JsonArray>();
|
||||||
meterCount = min((int)arr.size(), MAX_METERS);
|
meterCount = min((int)arr.size(), MAX_METERS);
|
||||||
for (int i = 0; i < meterCount; i++) {
|
for (int i = 0; i < meterCount; i++) {
|
||||||
@@ -97,6 +110,10 @@ static void saveConfig() {
|
|||||||
mq["pass"] = mqttCfg.pass;
|
mq["pass"] = mqttCfg.pass;
|
||||||
mq["prefix"] = mqttCfg.prefix;
|
mq["prefix"] = mqttCfg.prefix;
|
||||||
|
|
||||||
|
JsonObject auth = doc["auth"].to<JsonObject>();
|
||||||
|
auth["en"] = authCfg.enabled;
|
||||||
|
auth["pass"] = authCfg.pass;
|
||||||
|
|
||||||
JsonArray arr = doc["meters"].to<JsonArray>();
|
JsonArray arr = doc["meters"].to<JsonArray>();
|
||||||
for (int i = 0; i < meterCount; i++) {
|
for (int i = 0; i < meterCount; i++) {
|
||||||
JsonObject m = arr.add<JsonObject>();
|
JsonObject m = arr.add<JsonObject>();
|
||||||
@@ -346,11 +363,19 @@ static String escHtml(const String& s) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool requireAuth() {
|
||||||
|
if (!authCfg.enabled) return true;
|
||||||
|
if (server.authenticate(AUTH_USERNAME, authCfg.pass)) return true;
|
||||||
|
server.requestAuthentication();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Web handlers
|
// Web handlers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
static void handleRoot() {
|
static void handleRoot() {
|
||||||
|
if (!requireAuth()) { Serial.println("[HTTP] GET / -> 401"); return; }
|
||||||
Serial.println("[HTTP] GET /");
|
Serial.println("[HTTP] GET /");
|
||||||
String meterRows;
|
String meterRows;
|
||||||
for (int i = 0; i < meterCount; i++) {
|
for (int i = 0; i < meterCount; i++) {
|
||||||
@@ -385,6 +410,21 @@ static void handleRoot() {
|
|||||||
countOpts += ">" + String(i) + "</option>";
|
countOpts += ">" + String(i) + "</option>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String authSection;
|
||||||
|
{
|
||||||
|
String checked = authCfg.enabled ? " checked" : "";
|
||||||
|
const char* passHint = strlen(authCfg.pass) > 0 ? "set — leave blank to keep" : "enter password";
|
||||||
|
|
||||||
|
authSection +=
|
||||||
|
"<fieldset><legend>Security</legend>"
|
||||||
|
"<div class=row>"
|
||||||
|
"<label class=tgl-lbl>Require password</label>"
|
||||||
|
"<label class=switch><input type=checkbox name=auth_en" + checked + "><span class=slid></span></label>"
|
||||||
|
"</div>"
|
||||||
|
"<div class=row><label>Password</label><input name=auth_pass type=password placeholder='" + String(passHint) + "'></div>";
|
||||||
|
authSection += "</fieldset>";
|
||||||
|
}
|
||||||
|
|
||||||
String mqttSection;
|
String mqttSection;
|
||||||
{
|
{
|
||||||
char portStr[8];
|
char portStr[8];
|
||||||
@@ -437,10 +477,10 @@ static void handleRoot() {
|
|||||||
html += ".count-row label{margin-bottom:4px}";
|
html += ".count-row label{margin-bottom:4px}";
|
||||||
html += ".btn{width:100%;margin-top:8px;padding:12px;background:#4361ee;color:#fff;border:none;border-radius:8px;font-size:15px;font-weight:600;cursor:pointer;transition:background .2s}";
|
html += ".btn{width:100%;margin-top:8px;padding:12px;background:#4361ee;color:#fff;border:none;border-radius:8px;font-size:15px;font-weight:600;cursor:pointer;transition:background .2s}";
|
||||||
html += ".btn:hover{background:#3651d4}.mac{font-size:12px;color:#999;text-align:center;margin-top:20px}";
|
html += ".btn:hover{background:#3651d4}.mac{font-size:12px;color:#999;text-align:center;margin-top:20px}";
|
||||||
html += ".tgl-lbl{margin-bottom:0!important;line-height:28px}.switch{position:relative;display:inline-block;width:44px;height:24px;margin-left:auto;flex-shrink:0}";
|
html += ".tgl-lbl{margin-bottom:0!important;line-height:20px}.switch{position:relative;display:inline-block;width:36px;height:20px;margin-left:auto;flex-shrink:0}";
|
||||||
html += ".switch input{opacity:0;width:0;height:0}.slid{position:absolute;cursor:pointer;inset:0;background:#ccc;border-radius:24px;transition:.3s}";
|
html += ".switch input{opacity:0;width:0;height:0}.slid{position:absolute;cursor:pointer;inset:0;background:#ccc;border-radius:20px;transition:.3s}";
|
||||||
html += ".slid::before{content:\"\";position:absolute;height:18px;width:18px;left:3px;bottom:3px;background:#fff;border-radius:50%;transition:.3s}";
|
html += ".slid::before{content:\"\";position:absolute;height:16px;width:16px;left:2px;bottom:2px;background:#fff;border-radius:50%;transition:.3s}";
|
||||||
html += ".switch input:checked+.slid{background:#4361ee}.switch input:checked+.slid::before{transform:translateX(20px)}";
|
html += ".switch input:checked+.slid{background:#4361ee}.switch input:checked+.slid::before{transform:translateX(16px)}";
|
||||||
html += "</style></head><body><div class=card>";
|
html += "</style></head><body><div class=card>";
|
||||||
html += "<h1>M1730</h1><div class=sub>Configuration</div><div class=info>";
|
html += "<h1>M1730</h1><div class=sub>Configuration</div><div class=info>";
|
||||||
html += "<div><span>Hostname</span><span>" + String(hostname) + "</span></div>";
|
html += "<div><span>Hostname</span><span>" + String(hostname) + "</span></div>";
|
||||||
@@ -450,6 +490,7 @@ static void handleRoot() {
|
|||||||
html += "<div class=count-row><div><label>Meters</label>";
|
html += "<div class=count-row><div><label>Meters</label>";
|
||||||
html += "<select name=meter_count onchange='this.form.submit()'>" + countOpts + "</select></div></div>";
|
html += "<select name=meter_count onchange='this.form.submit()'>" + countOpts + "</select></div></div>";
|
||||||
html += meterRows;
|
html += meterRows;
|
||||||
|
html += authSection;
|
||||||
html += mqttSection;
|
html += mqttSection;
|
||||||
html += "<button class=btn type=submit>Save</button></form>";
|
html += "<button class=btn type=submit>Save</button></form>";
|
||||||
html += "<div class=mac>" + WiFi.macAddress() + "</div></div>";
|
html += "<div class=mac>" + WiFi.macAddress() + "</div></div>";
|
||||||
@@ -468,6 +509,7 @@ static void handleRoot() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void handleConfig() {
|
static void handleConfig() {
|
||||||
|
if (!requireAuth()) { Serial.println("[HTTP] POST /config -> 401"); return; }
|
||||||
Serial.println("[HTTP] POST /config");
|
Serial.println("[HTTP] POST /config");
|
||||||
if (server.hasArg("hostname")) {
|
if (server.hasArg("hostname")) {
|
||||||
String val = server.arg("hostname");
|
String val = server.arg("hostname");
|
||||||
@@ -495,6 +537,16 @@ static void handleConfig() {
|
|||||||
Serial.println("[HTTP] mqtt disabled");
|
Serial.println("[HTTP] mqtt disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (server.hasArg("auth_en")) {
|
||||||
|
if (server.hasArg("auth_pass") && server.arg("auth_pass").length() > 0)
|
||||||
|
strlcpy(authCfg.pass, server.arg("auth_pass").c_str(), sizeof(authCfg.pass));
|
||||||
|
authCfg.enabled = strlen(authCfg.pass) > 0;
|
||||||
|
Serial.printf("[HTTP] auth enabled=%d\n", authCfg.enabled);
|
||||||
|
} else {
|
||||||
|
authCfg.enabled = false;
|
||||||
|
Serial.println("[HTTP] auth disabled");
|
||||||
|
}
|
||||||
|
|
||||||
int newCount = meterCount;
|
int newCount = meterCount;
|
||||||
if (server.hasArg("meter_count"))
|
if (server.hasArg("meter_count"))
|
||||||
newCount = constrain(server.arg("meter_count").toInt(), 1, MAX_METERS);
|
newCount = constrain(server.arg("meter_count").toInt(), 1, MAX_METERS);
|
||||||
@@ -536,6 +588,7 @@ static void handleConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void handleSet() {
|
static void handleSet() {
|
||||||
|
if (!requireAuth()) { Serial.println("[HTTP] GET /set -> 401"); return; }
|
||||||
if (!server.hasArg("i") || !server.hasArg("v")) { server.send(400); Serial.println("[HTTP] GET /set missing args"); return; }
|
if (!server.hasArg("i") || !server.hasArg("v")) { server.send(400); Serial.println("[HTTP] GET /set missing args"); return; }
|
||||||
int idx = server.arg("i").toInt();
|
int idx = server.arg("i").toInt();
|
||||||
float val = server.arg("v").toFloat();
|
float val = server.arg("v").toFloat();
|
||||||
|
|||||||
Reference in New Issue
Block a user