Login and Token System implemented
This commit is contained in:
		
							parent
							
								
									8f2444caeb
								
							
						
					
					
						commit
						bb3e4fa27c
					
				
							
								
								
									
										
											BIN
										
									
								
								data/admin
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/admin
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2
									
								
								data/s/bundle.1727d.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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
											
										
									
								
							| @ -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
									
								
							
							
						
						
									
										59
									
								
								src/AdminAuth.cpp
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										77
									
								
								src/AdminAuth.h
									
									
									
									
									
										Normal 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(); | ||||
|     }; | ||||
| } | ||||
| @ -46,6 +46,8 @@ namespace userdb | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     class UserDb | ||||
|     { | ||||
|     private: | ||||
| @ -148,7 +150,8 @@ namespace userdb | ||||
|             { | ||||
|                 close_file(); | ||||
|             } | ||||
|             void close_file(){ | ||||
|             void close_file() | ||||
|             { | ||||
|                 db_file.close(); | ||||
|             } | ||||
|             bool has_next() | ||||
|  | ||||
| @ -2,7 +2,9 @@ | ||||
| 
 | ||||
| using namespace webconsole; | ||||
| 
 | ||||
| WebConsole::WebConsole() {} | ||||
| WebConsole::WebConsole() | ||||
| { | ||||
| } | ||||
| WebConsole::~WebConsole() | ||||
| { | ||||
|     _server->close(); | ||||
| @ -12,7 +14,11 @@ bool WebConsole::init(userdb::UserDb *userdb) | ||||
| { | ||||
|     _server = new ESP8266WebServer(80); | ||||
|     this->userdb = userdb; | ||||
|     const char *headerkeys[] = {"Authentification"}; | ||||
|     size_t headerkeyssize = sizeof(headerkeys) / sizeof(char *); | ||||
|     _server->collectHeaders(headerkeys, headerkeyssize); | ||||
|     _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_GET, std::bind(&WebConsole::_getUserDb, this)); | ||||
|     _server->on("/api/rfid", std::bind(&WebConsole::_catchRFID, this)); | ||||
| @ -45,6 +51,48 @@ bool WebConsole::isInterceptingRfid() | ||||
| { | ||||
|     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() | ||||
| { | ||||
|     _server->sendHeader("Access-Control-Allow-Origin", "*"); | ||||
| @ -54,9 +102,9 @@ void WebConsole::_sendCORS() | ||||
| } | ||||
| void WebConsole::_handleUnknown() | ||||
| { | ||||
|     _sendCORS(); | ||||
|     if (_server->method() == HTTP_OPTIONS) | ||||
|     { | ||||
|         _sendCORS(); | ||||
|         _server->send(204); | ||||
|     } | ||||
|     else | ||||
| @ -70,6 +118,8 @@ void WebConsole::_handleUnknown() | ||||
| void WebConsole::_getUserDb() | ||||
| { | ||||
|     _sendCORS(); | ||||
|     if (!_isAuth()) | ||||
|         return; | ||||
|     File src = LittleFS.open("userdb.csv", "r"); | ||||
|     if (src) | ||||
|     { | ||||
| @ -80,6 +130,8 @@ void WebConsole::_getUserDb() | ||||
| void WebConsole::_deleteUser() | ||||
| { | ||||
|     _sendCORS(); | ||||
|     if (!_isAuth()) | ||||
|         return; | ||||
|     if (userdb == nullptr) | ||||
|     { | ||||
|         _server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}"); | ||||
| @ -101,6 +153,8 @@ void WebConsole::_deleteUser() | ||||
| void WebConsole::_getUser() | ||||
| { | ||||
|     _sendCORS(); | ||||
|     if (!_isAuth()) | ||||
|         return; | ||||
|     if (userdb == nullptr) | ||||
|     { | ||||
|         _server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}"); | ||||
| @ -120,6 +174,8 @@ void WebConsole::_getUser() | ||||
| void WebConsole::_updateUser() | ||||
| { | ||||
|     _sendCORS(); | ||||
|     if (!_isAuth()) | ||||
|         return; | ||||
|     userdb::User updated; | ||||
|     String body = _server->arg("plain"); | ||||
|     const int capacity = 256; | ||||
| @ -172,6 +228,8 @@ void WebConsole::_updateUser() | ||||
| void WebConsole::_createUser() | ||||
| { | ||||
|     _sendCORS(); | ||||
|     if (!_isAuth()) | ||||
|         return; | ||||
|     userdb::User created; | ||||
|     String body = _server->arg("plain"); | ||||
|     const int capacity = 1024; | ||||
| @ -207,6 +265,8 @@ void WebConsole::_createUser() | ||||
| void WebConsole::_dropUserDb() | ||||
| { | ||||
|     _sendCORS(); | ||||
|     if (!_isAuth()) | ||||
|         return; | ||||
|     if (userdb->drop()) | ||||
|         _server->send(500, "text/json", "{\"ok\":\"UserDb dropped.\"}"); | ||||
|     else | ||||
| @ -215,6 +275,8 @@ void WebConsole::_dropUserDb() | ||||
| void WebConsole::_catchRFID() | ||||
| { | ||||
|     _sendCORS(); | ||||
|     if (!_isAuth()) | ||||
|         return; | ||||
|     if (rfid == nullptr) | ||||
|     { | ||||
|         _server->send(500, "text/json", "{\"error\":\"RFID not attached.\"}"); | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include "ArduinoJson.h" | ||||
| #include "UserDb.h" | ||||
| #include "Rfid.h" | ||||
| #include "AdminAuth.h" | ||||
| namespace webconsole | ||||
| { | ||||
|     static const char path_prefix[] PROGMEM = "/s"; | ||||
| @ -24,6 +25,8 @@ namespace webconsole | ||||
| 
 | ||||
|     private: | ||||
|         void _sendCORS(); | ||||
|         void _auth(); | ||||
|         bool _isAuth(); | ||||
|         void _handleUnknown(); | ||||
|         void _getUserDb(); | ||||
|         void _deleteUser(); | ||||
| @ -49,6 +52,7 @@ namespace webconsole | ||||
|         bool catch_rfid_updated = false; | ||||
|         String rfid_buffer; | ||||
|         Rfid *rfid = nullptr; | ||||
|         AdminAuth auth; | ||||
|     }; | ||||
| 
 | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user