Login and Token System implemented

This commit is contained in:
Jean Jacques Avril 2022-03-10 16:37:02 +01:00
parent 8f2444caeb
commit bb3e4fa27c
9 changed files with 212 additions and 7 deletions

Binary file not shown.

2
data/s/bundle.1727d.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>doorlock_pwa</title><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-capable" content="yes"><link rel="apple-touch-icon" href="/assets/icons/apple-touch-icon.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#673ab8"><style>*{box-sizing:border-box}html{font-family:Helvetica,sans-serif;font-size:16px}body,html{height:100%}body{background-color:#fff;margin:0;padding:0;width:100%}</style><link href="/bundle.45d14.css" rel="stylesheet" media="only x" onload="this.media='all'"><noscript><link rel="stylesheet" href="/bundle.45d14.css"></noscript></head><body><script defer="defer" src="/bundle.f9ad8.js"></script><script nomodule="" src="/polyfills.058fb.js"></script></body></html> <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>doorlock_pwa</title><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-capable" content="yes"><link rel="apple-touch-icon" href="/assets/icons/apple-touch-icon.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#673ab8"><style>*{box-sizing:border-box}html{font-family:Helvetica,sans-serif;font-size:16px}body,html{height:100%}body{background-color:#fff;margin:0;padding:0;width:100%}</style><link href="/bundle.45d14.css" rel="stylesheet" media="only x" onload="this.media='all'"><noscript><link rel="stylesheet" href="/bundle.45d14.css"></noscript></head><body><script defer="defer" src="/bundle.1727d.js"></script><script nomodule="" src="/polyfills.058fb.js"></script></body></html>

59
src/AdminAuth.cpp Normal file
View File

@ -0,0 +1,59 @@
#include "AdminAuth.h"
using namespace webconsole;
bool AdminAuth::isAuth(const char *token)
{
return tokenbuffer->exists(token) != -1;
}
bool AdminAuth::logout(const char *token)
{
int16_t tid = tokenbuffer->exists(token);
tokenbuffer->setnull(tid);
return tid != -1;
}
char *AdminAuth::login(const char *username, const char *password)
{
char *res = nullptr;
File adminfile = LittleFS.open("admin", "r");
bool current_field = false;
uint16_t current_pos = 0;
Serial.print('-');
while (adminfile.available())
{
char current = adminfile.read();
Serial.print(current);
Serial.print('-');
if (current == 0x00)
{
if (current_field)
{
if (password[current_pos] == 0x00)
res = tokenbuffer->newToken();
break;
}
else
{
if (username[current_pos] != 0x00)
break;
current_pos = 0;
current_field = true;
}
}
else if (!current_field)
{
Serial.print(String(username[current_pos]));
if (username[current_pos] == 0x00 || username[current_pos] != current)
break;
current_pos++;
}
else
{
Serial.print(String(password[current_pos]));
if (password[current_pos] == 0x00 || password[current_pos] != current)
break;
current_pos++;
}
Serial.print(',');
}
adminfile.close();
return res;
}

77
src/AdminAuth.h Normal file
View File

@ -0,0 +1,77 @@
#pragma once
#include "LittleFS.h"
#define TOKENBUFFERCAPACITY 3
#define TOKENLENGHT 10
namespace webconsole
{
struct TokenBuffer
{
TokenBuffer()
{
for (uint16_t j = 0; j < TOKENBUFFERCAPACITY; j++)
setnull(j);
}
char *newToken()
{
if (next >= TOKENBUFFERCAPACITY)
next = 0;
for (uint16_t i = 0; i < TOKENLENGHT; i++)
token[next][i] = randomChar();
token[next][TOKENLENGHT] = 0x00;
return &token[next++][0];
}
int16_t exists(const char *cmp)
{
for (uint16_t j = 0; j < TOKENBUFFERCAPACITY; j++)
{
bool match = true;
for (uint16_t i = 0; match && i < TOKENLENGHT; i++)
if (token[j][i] != cmp[i])
match = false;
if (match)
return j;
}
return -1;
}
void setnull(uint16_t index)
{
if (index >= 0 && index < TOKENBUFFERCAPACITY)
for (uint16_t i = 0; i < TOKENLENGHT; i++)
token[index][i] = 0x00; // initialize with nullbytes
}
private:
uint16_t next = 0;
char token[TOKENBUFFERCAPACITY][TOKENLENGHT + 1];
char randomChar()
{
auto charrype = random(0, 3);
switch (charrype)
{
case 0:
return random(0x30, 0x3A); // Numbers
break;
case 1:
return random(0x41, 0x5B); // Capital Letters
break;
case 2:
return random(0x61, 0x7B); // Small Letters
break;
}
return 0x21;
}
};
class AdminAuth
{
public:
char *login(const char *username, const char *password);
bool isAuth(const char *token);
bool logout(const char *token);
private:
TokenBuffer *tokenbuffer = new TokenBuffer();
};
}

View File

@ -46,6 +46,8 @@ namespace userdb
} }
}; };
class UserDb class UserDb
{ {
private: private:
@ -148,7 +150,8 @@ namespace userdb
{ {
close_file(); close_file();
} }
void close_file(){ void close_file()
{
db_file.close(); db_file.close();
} }
bool has_next() bool has_next()

View File

@ -2,7 +2,9 @@
using namespace webconsole; using namespace webconsole;
WebConsole::WebConsole() {} WebConsole::WebConsole()
{
}
WebConsole::~WebConsole() WebConsole::~WebConsole()
{ {
_server->close(); _server->close();
@ -12,7 +14,11 @@ bool WebConsole::init(userdb::UserDb *userdb)
{ {
_server = new ESP8266WebServer(80); _server = new ESP8266WebServer(80);
this->userdb = userdb; this->userdb = userdb;
const char *headerkeys[] = {"Authentification"};
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char *);
_server->collectHeaders(headerkeys, headerkeyssize);
_server->begin(); _server->begin();
_server->on("/api/auth", HTTPMethod::HTTP_POST, std::bind(&WebConsole::_auth, this));
_server->on("/api/userdb", HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_dropUserDb, this)); _server->on("/api/userdb", HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_dropUserDb, this));
_server->on("/api/userdb", HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUserDb, this)); _server->on("/api/userdb", HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUserDb, this));
_server->on("/api/rfid", std::bind(&WebConsole::_catchRFID, this)); _server->on("/api/rfid", std::bind(&WebConsole::_catchRFID, this));
@ -45,6 +51,48 @@ bool WebConsole::isInterceptingRfid()
{ {
return catch_rfid; return catch_rfid;
} }
bool WebConsole::_isAuth()
{
if (!_server->hasHeader("Authentification"))
{
_server->send(401, "text/plain", "Error 401: Unauthorized (missing auth token)");
return false;
}
const char *token = _server->header("Authentification").c_str();
bool res = auth.isAuth(token);
if (!res)
_server->send(401, "text/plain", "Error 401: Unauthorized (missing auth token)");
return res;
}
void WebConsole::_auth()
{
_sendCORS();
String action = _server->arg("action");
if (action.equals("check"))
{
const char *token = _server->arg("token").c_str();
bool res = auth.isAuth(token);
_server->send(200, "text/plain", res ? "valid" : "invalid");
}
else if (action.equals("login"))
{
const char *username = _server->arg("username").c_str();
const char *password = _server->arg("password").c_str();
char *token = auth.login(username, password);
if (token == nullptr)
_server->send(401, "text/plain", "failed!");
else
_server->send(200, "text/plain", token);
}
else if (action.equals("logout"))
{
const char *token = _server->arg("token").c_str();
bool res = auth.logout(token);
_server->send(200, "text/plain", res ? "success" : "failed");
}
else
_server->send(404, "text/plain", "unknown action");
}
void WebConsole::_sendCORS() void WebConsole::_sendCORS()
{ {
_server->sendHeader("Access-Control-Allow-Origin", "*"); _server->sendHeader("Access-Control-Allow-Origin", "*");
@ -54,9 +102,9 @@ void WebConsole::_sendCORS()
} }
void WebConsole::_handleUnknown() void WebConsole::_handleUnknown()
{ {
_sendCORS();
if (_server->method() == HTTP_OPTIONS) if (_server->method() == HTTP_OPTIONS)
{ {
_sendCORS();
_server->send(204); _server->send(204);
} }
else else
@ -70,6 +118,8 @@ void WebConsole::_handleUnknown()
void WebConsole::_getUserDb() void WebConsole::_getUserDb()
{ {
_sendCORS(); _sendCORS();
if (!_isAuth())
return;
File src = LittleFS.open("userdb.csv", "r"); File src = LittleFS.open("userdb.csv", "r");
if (src) if (src)
{ {
@ -80,6 +130,8 @@ void WebConsole::_getUserDb()
void WebConsole::_deleteUser() void WebConsole::_deleteUser()
{ {
_sendCORS(); _sendCORS();
if (!_isAuth())
return;
if (userdb == nullptr) if (userdb == nullptr)
{ {
_server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}"); _server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}");
@ -101,6 +153,8 @@ void WebConsole::_deleteUser()
void WebConsole::_getUser() void WebConsole::_getUser()
{ {
_sendCORS(); _sendCORS();
if (!_isAuth())
return;
if (userdb == nullptr) if (userdb == nullptr)
{ {
_server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}"); _server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}");
@ -120,6 +174,8 @@ void WebConsole::_getUser()
void WebConsole::_updateUser() void WebConsole::_updateUser()
{ {
_sendCORS(); _sendCORS();
if (!_isAuth())
return;
userdb::User updated; userdb::User updated;
String body = _server->arg("plain"); String body = _server->arg("plain");
const int capacity = 256; const int capacity = 256;
@ -172,6 +228,8 @@ void WebConsole::_updateUser()
void WebConsole::_createUser() void WebConsole::_createUser()
{ {
_sendCORS(); _sendCORS();
if (!_isAuth())
return;
userdb::User created; userdb::User created;
String body = _server->arg("plain"); String body = _server->arg("plain");
const int capacity = 1024; const int capacity = 1024;
@ -207,6 +265,8 @@ void WebConsole::_createUser()
void WebConsole::_dropUserDb() void WebConsole::_dropUserDb()
{ {
_sendCORS(); _sendCORS();
if (!_isAuth())
return;
if (userdb->drop()) if (userdb->drop())
_server->send(500, "text/json", "{\"ok\":\"UserDb dropped.\"}"); _server->send(500, "text/json", "{\"ok\":\"UserDb dropped.\"}");
else else
@ -215,6 +275,8 @@ void WebConsole::_dropUserDb()
void WebConsole::_catchRFID() void WebConsole::_catchRFID()
{ {
_sendCORS(); _sendCORS();
if (!_isAuth())
return;
if (rfid == nullptr) if (rfid == nullptr)
{ {
_server->send(500, "text/json", "{\"error\":\"RFID not attached.\"}"); _server->send(500, "text/json", "{\"error\":\"RFID not attached.\"}");

View File

@ -7,6 +7,7 @@
#include "ArduinoJson.h" #include "ArduinoJson.h"
#include "UserDb.h" #include "UserDb.h"
#include "Rfid.h" #include "Rfid.h"
#include "AdminAuth.h"
namespace webconsole namespace webconsole
{ {
static const char path_prefix[] PROGMEM = "/s"; static const char path_prefix[] PROGMEM = "/s";
@ -24,6 +25,8 @@ namespace webconsole
private: private:
void _sendCORS(); void _sendCORS();
void _auth();
bool _isAuth();
void _handleUnknown(); void _handleUnknown();
void _getUserDb(); void _getUserDb();
void _deleteUser(); void _deleteUser();
@ -49,6 +52,7 @@ namespace webconsole
bool catch_rfid_updated = false; bool catch_rfid_updated = false;
String rfid_buffer; String rfid_buffer;
Rfid *rfid = nullptr; Rfid *rfid = nullptr;
AdminAuth auth;
}; };
} }