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 |     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() | ||||||
| @ -164,7 +167,7 @@ namespace userdb | |||||||
|                         if (matchline == line) |                         if (matchline == line) | ||||||
|                         { |                         { | ||||||
|                             current = read_csv_line(current, db_file, line, match, filter_attr); |                             current = read_csv_line(current, db_file, line, match, filter_attr); | ||||||
|                             current.match=true; |                             current.match = true; | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     else |                     else | ||||||
|  | |||||||
| @ -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.\"}"); | ||||||
|  | |||||||
| @ -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; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user