Skip to content

Commit

Permalink
Simplify and fix web auth code (#284)
Browse files Browse the repository at this point in the history
  • Loading branch information
xoseperez committed Nov 21, 2017
1 parent 9cf2579 commit b7e68c4
Show file tree
Hide file tree
Showing 6 changed files with 2,955 additions and 3,031 deletions.
Binary file modified code/espurna/data/index.html.gz
Binary file not shown.
5,851 changes: 2,922 additions & 2,929 deletions code/espurna/static/index.html.gz.h

Large diffs are not rendered by default.

30 changes: 18 additions & 12 deletions code/espurna/web.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{}

Expand All @@ -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) {
Expand All @@ -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();

}
}
}
Expand Down
72 changes: 14 additions & 58 deletions code/espurna/web.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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];

// -----------------------------------------------------------------------------

Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -389,6 +386,7 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) {
// Save settings
if (save) {

wsConfigure();
saveSettings();
wifiConfigure();
otaConfigure();
Expand Down Expand Up @@ -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());
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -884,7 +861,6 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) {
void _onAPIs(AsyncWebServerRequest *request) {

_webLog(request);

if (!_authAPI(request)) return;

bool asJson = _asJson(request);
Expand All @@ -911,7 +887,6 @@ void _onAPIs(AsyncWebServerRequest *request) {
void _onRPC(AsyncWebServerRequest *request) {

_webLog(request);

if (!_authAPI(request)) return;

//bool asJson = _asJson(request);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)) {

Expand Down Expand Up @@ -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"));
Expand Down
29 changes: 1 addition & 28 deletions code/html/custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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();

}

Expand Down
4 changes: 0 additions & 4 deletions code/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@

<body>

<div id="credentials" class="webmode">
Wrong credentials
</div>

<div id="password" class="webmode">

<div class="content">
Expand Down

0 comments on commit b7e68c4

Please sign in to comment.