Browse Source

Simplify and fix web auth code (#284)

fastled
Xose Pérez 6 years ago
parent
commit
b7e68c4856
6 changed files with 2955 additions and 3031 deletions
  1. BIN
      code/espurna/data/index.html.gz
  2. +2922
    -2929
      code/espurna/static/index.html.gz.h
  3. +18
    -12
      code/espurna/web.h
  4. +14
    -58
      code/espurna/web.ino
  5. +1
    -28
      code/html/custom.js
  6. +0
    -4
      code/html/index.html

BIN
code/espurna/data/index.html.gz View File


+ 2922
- 2929
code/espurna/static/index.html.gz.h
File diff suppressed because it is too large
View File


+ 18
- 12
code/espurna/web.h View File

@ -19,8 +19,8 @@ class WebSocketIncommingBuffer {
public:
WebSocketIncommingBuffer(AwsMessageHandler cb, bool terminate_string = true, bool cb_on_fragments = false) :
_cb(cb),
_cb_on_fragments(cb_on_fragments),
_terminate_string(terminate_string),
_cb_on_fragments(cb_on_fragments),
_buffer(0)
{}
@ -30,19 +30,24 @@ class WebSocketIncommingBuffer {
void data_event(AsyncWebSocketClient *client, AwsFrameInfo *info, uint8_t *data, size_t len) {
if((info->final || _cb_on_fragments) &&
!_terminate_string && info->index == 0 && info->len == len) {
if ((info->final || _cb_on_fragments)
&& !_terminate_string
&& info->index == 0
&& info->len == len) {
/* The whole message is in a single frame and we got all of it's
data therefore we can parse it without copying the data first.*/
_cb(client, data, len);
} else {
if (info->len > MAX_WS_MSG_SIZE) return;
/* Check if previous fragment was discarded because it was too long. */
if (!_cb_on_fragments && info->num > 0 && !_buffer) return;
//if (!_cb_on_fragments && info->num > 0 && !_buffer) return;
if (!_buffer) _buffer = new std::vector<uint8_t>();
if (!_buffer) {
_buffer = new std::vector<uint8_t>();
}
if (info->index == 0) {
//New frame => preallocate memory
if (_cb_on_fragments) {
@ -59,16 +64,17 @@ class WebSocketIncommingBuffer {
}
}
}
//assert(_buffer->size() == info->index);
_buffer->insert(_buffer->end(), data, data+len);
if (info->index + len == info->len &&
(info->final || _cb_on_fragments)) {
if (info->index + len == info->len
&& (info->final || _cb_on_fragments)) {
// Frame/message complete
if (_terminate_string) {
_buffer->push_back(0);
}
if (_terminate_string) _buffer->push_back(0);
_cb(client, _buffer->data(), _buffer->size());
_buffer->clear();
}
}
}


+ 14
- 58
code/espurna/web.ino View File

@ -36,11 +36,6 @@ Ticker _web_defer;
// -----------------------------------------------------------------------------
AsyncWebSocket _ws("/ws");
typedef struct {
IPAddress ip;
unsigned long timestamp = 0;
} ws_ticket_t;
ws_ticket_t _ticket[WS_BUFFER_SIZE];
// -----------------------------------------------------------------------------
@ -70,6 +65,8 @@ void _wsMQTTCallback(unsigned int type, const char * topic, const char * payload
void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) {
//DEBUG_MSG_P(PSTR("[WEBSOCKET] Parsing: %s\n"), length ? (char*) payload : "");
// Get client ID
uint32_t client_id = client->id();
@ -389,6 +386,7 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) {
// Save settings
if (save) {
wsConfigure();
saveSettings();
wifiConfigure();
otaConfigure();
@ -706,35 +704,9 @@ void _wsStart(uint32_t client_id) {
}
bool _wsAuth(AsyncWebSocketClient * client) {
IPAddress ip = client->remoteIP();
unsigned long now = millis();
unsigned short index = 0;
for (index = 0; index < WS_BUFFER_SIZE; index++) {
if ((_ticket[index].ip == ip) && (now - _ticket[index].timestamp < WS_TIMEOUT)) break;
}
if (index == WS_BUFFER_SIZE) {
DEBUG_MSG_P(PSTR("[WEBSOCKET] Validation check failed\n"));
wsSend_P(client->id(), PSTR("{\"message\": 10}"));
return false;
}
return true;
}
void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
if (type == WS_EVT_CONNECT) {
// Authorize
#ifndef NOWSAUTH
if (!_wsAuth(client)) return;
#endif
IPAddress ip = client->remoteIP();
DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u connected, ip: %d.%d.%d.%d, url: %s\n"), client->id(), ip[0], ip[1], ip[2], ip[3], server->url());
_wsStart(client->id());
@ -755,6 +727,7 @@ void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTy
DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u pong(%u): %s\n"), client->id(), len, len ? (char*) data : "");
} else if(type == WS_EVT_DATA) {
//DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u data(%u): %s\n"), client->id(), len, len ? (char*) data : "");
WebSocketIncommingBuffer *buffer = (WebSocketIncommingBuffer *)client->_tempObject;
AwsFrameInfo * info = (AwsFrameInfo*)arg;
buffer->data_event(client, info, data, len);
@ -794,11 +767,15 @@ void wsSend_P(uint32_t client_id, PGM_P payload) {
_ws.text(client_id, buffer);
}
void wsConfigure() {
_ws.setAuthentication(WEB_USERNAME, (const char *) getSetting("adminPass", ADMIN_PASS).c_str());
}
void wsSetup() {
_ws.onEvent(_wsEvent);
mqttRegister(_wsMQTTCallback);
wsConfigure();
_server->addHandler(&_ws);
_server->on("/auth", HTTP_GET, _onAuth);
mqttRegister(_wsMQTTCallback);
}
// -----------------------------------------------------------------------------
@ -884,7 +861,6 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) {
void _onAPIs(AsyncWebServerRequest *request) {
_webLog(request);
if (!_authAPI(request)) return;
bool asJson = _asJson(request);
@ -911,7 +887,6 @@ void _onAPIs(AsyncWebServerRequest *request) {
void _onRPC(AsyncWebServerRequest *request) {
_webLog(request);
if (!_authAPI(request)) return;
//bool asJson = _asJson(request);
@ -978,29 +953,6 @@ bool _authenticate(AsyncWebServerRequest *request) {
return request->authenticate(WEB_USERNAME, httpPassword);
}
void _onAuth(AsyncWebServerRequest *request) {
_webLog(request);
if (!_authenticate(request)) return request->requestAuthentication();
IPAddress ip = request->client()->remoteIP();
unsigned long now = millis();
unsigned short index;
for (index = 0; index < WS_BUFFER_SIZE; index++) {
if (_ticket[index].ip == ip) break;
if (_ticket[index].timestamp == 0) break;
if (now - _ticket[index].timestamp > WS_TIMEOUT) break;
}
if (index == WS_BUFFER_SIZE) {
request->send(429);
} else {
_ticket[index].ip = ip;
_ticket[index].timestamp = now;
request->send(204);
}
}
void _onGetConfig(AsyncWebServerRequest *request) {
_webLog(request);
@ -1031,6 +983,7 @@ void _onGetConfig(AsyncWebServerRequest *request) {
void _onHome(AsyncWebServerRequest *request) {
_webLog(request);
if (!_authenticate(request)) return request->requestAuthentication();
if (request->header("If-Modified-Since").equals(_last_modified)) {
@ -1129,6 +1082,9 @@ int _onCertificate(void * arg, const char *filename, uint8_t **buf) {
void _onUpgrade(AsyncWebServerRequest *request) {
_webLog(request);
if (!_authenticate(request)) return request->requestAuthentication();
char buffer[10];
if (!Update.hasError()) {
sprintf_P(buffer, PSTR("OK"));


+ 1
- 28
code/html/custom.js View File

@ -90,24 +90,6 @@ function generateAPIKey() {
return false;
}
function forgetCredentials() {
$.ajax({
'method': 'GET',
'url': '/',
'async': false,
'username': "logmeout",
'password': "123456",
'headers': { "Authorization": "Basic xxx" }
}).done(function(data) {
return false;
// If we don't get an error, we actually got an error as we expect an 401!
}).fail(function(){
// We expect to get an 401 Unauthorized error! In this case we are successfully
// logged out and we redirect the user.
return true;
});
}
function getJson(str) {
try {
return JSON.parse(str);
@ -625,14 +607,12 @@ function processData(data) {
password = data.webMode == 1;
$("#layout").toggle(data.webMode == 0);
$("#password").toggle(data.webMode == 1);
$("#credentials").hide();
}
// Actions
if (key == "action") {
if (data.action == "reload") {
if (password) forgetCredentials();
doReload(1000);
}
@ -955,14 +935,7 @@ function init() {
$(document).on('change', 'input', hasChanged);
$(document).on('change', 'select', hasChanged);
$.ajax({
'method': 'GET',
'url': window.location.href + 'auth'
}).done(function(data) {
connect();
}).fail(function(){
$("#credentials").show();
});
connect();
}


+ 0
- 4
code/html/index.html View File

@ -21,10 +21,6 @@
<body>
<div id="credentials" class="webmode">
Wrong credentials
</div>
<div id="password" class="webmode">
<div class="content">


Loading…
Cancel
Save