/* authentication_service.cpp - ESP3D authentication service class Copyright (c) 2014 Luc Lebosse. All rights reserved. This code is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with This code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "authentication_service.h" #include "../../core/esp3doutput.h" #include "../../core/settings_esp3d.h" #if defined(AUTHENTICATION_FEATURE) #if defined(HTTP_FEATURE) #if defined(ARDUINO_ARCH_ESP32) #include #endif //ARDUINO_ARCH_ESP32 #if defined(ARDUINO_ARCH_ESP8266) #include #endif //ARDUINO_ARCH_ESP8266 Authwebserver *AuthenticationService::_webserver = nullptr; #endif //HTTP_FEATURE #endif //AUTHENTICATION_FEATURE #if defined(AUTHENTICATION_FEATURE) String AuthenticationService::_adminpwd = ""; String AuthenticationService::_userpwd = ""; #if defined(HTTP_FEATURE) uint32_t AuthenticationService::_sessionTimeout = 360000; auth_ip *AuthenticationService::_head = nullptr; uint8_t AuthenticationService::_current_nb_ip = 0; #endif //HTTP_FEATURE #endif //AUTHENTICATION_FEATURE #define MAX_AUTH_IP 10 //#define ALLOW_MULTIPLE_SESSIONS //check authentification level_authenticate_type AuthenticationService::authenticated_level(const char *pwd, ESP3DOutput *output) { #ifdef AUTHENTICATION_FEATURE level_authenticate_type auth_type = LEVEL_GUEST; if (pwd != nullptr) { if (isadmin(pwd)) { auth_type = LEVEL_ADMIN; } if (isuser(pwd) && (auth_type != LEVEL_ADMIN)) { auth_type = LEVEL_USER; } return auth_type; } else { if (output) { if (output->client() != ESP_HTTP_CLIENT) { return auth_type; } } #if defined(HTTP_FEATURE) if (_webserver) { if (_webserver->hasHeader("Authorization")) { //log_esp3d("Check authorization %",(_webserver->uri()).c_str()); if (_webserver->authenticate(DEFAULT_ADMIN_LOGIN, _adminpwd.c_str())) { auth_type = LEVEL_ADMIN; } else { if (_webserver->authenticate(DEFAULT_USER_LOGIN, _userpwd.c_str())) { auth_type = LEVEL_USER; } } } if (_webserver->hasHeader("Cookie")) { //log_esp3d("Check Cookie %s",(_webserver->uri()).c_str()); String cookie = _webserver->header("Cookie"); int pos = cookie.indexOf("ESPSESSIONID="); if (pos != -1) { int pos2 = cookie.indexOf(";", pos); String sessionID = cookie.substring(pos + strlen("ESPSESSIONID="), pos2); IPAddress ip = _webserver->client().remoteIP(); //check if cookie can be reset and clean table in same time auth_type = ResetAuthIP(ip, sessionID.c_str()); //log_esp3d("Authentication = %d", auth_type); } } } #endif //HTTP_FEATURE } return auth_type; #else (void)pwd; (void)output; return LEVEL_ADMIN; #endif //AUTHENTICATION_FEATURE } #ifdef AUTHENTICATION_FEATURE #if defined(HTTP_FEATURE) uint32_t AuthenticationService::setSessionTimeout(uint32_t timeout) { if (timeout >= 0) { _sessionTimeout = timeout; } return _sessionTimeout; } uint32_t AuthenticationService::getSessionTimeout() { return _sessionTimeout; } #endif //HTTP_FEATURE bool AuthenticationService::begin(Authwebserver *webserver) { end(); update(); #if defined(HTTP_FEATURE) _webserver = webserver; #endif //HTTP_FEATURE //value is in ms but storage is in min _sessionTimeout = 1000 * 60 * Settings_ESP3D::read_byte(ESP_SESSION_TIMEOUT); return true; } void AuthenticationService::end() { #if defined(HTTP_FEATURE) _webserver = nullptr; ClearAllSessions(); #endif //HTTP_FEATURE } void AuthenticationService::update() { _adminpwd = Settings_ESP3D::read_string(ESP_ADMIN_PWD); _userpwd = Settings_ESP3D::read_string(ESP_USER_PWD); } void AuthenticationService::handle() { } //check admin password bool AuthenticationService::isadmin(const char *pwd) { if (strcmp(_adminpwd.c_str(), pwd) != 0) { return false; } else { return true; } } //check user password - admin password is also valid bool AuthenticationService::isuser(const char *pwd) { //it is not user password if (strcmp(_userpwd.c_str(), pwd) != 0) { //check admin password return isadmin(pwd); } else { return true; } } #if defined(HTTP_FEATURE) //add the information in the linked list if possible bool AuthenticationService::AddAuthIP(auth_ip *item) { if (_current_nb_ip > MAX_AUTH_IP) { return false; } item->_next = _head; _head = item; _current_nb_ip++; return true; } //Session ID based on IP and time using 16 char char *AuthenticationService::create_session_ID() { static char sessionID[17]; //reset SESSIONID for (int i = 0; i < 17; i++) { sessionID[i] = '\0'; } //get time uint32_t now = millis(); //get remote IP IPAddress remoteIP = _webserver->client().remoteIP(); //generate SESSIONID if (0 > sprintf(sessionID, "%02X%02X%02X%02X%02X%02X%02X%02X", remoteIP[0], remoteIP[1], remoteIP[2], remoteIP[3], (uint8_t)((now >> 0) & 0xff), (uint8_t)((now >> 8) & 0xff), (uint8_t)((now >> 16) & 0xff), (uint8_t)((now >> 24) & 0xff))) { strcpy(sessionID, "NONE"); } return sessionID; } bool AuthenticationService::ClearAllSessions() { while (_head) { auth_ip *current = _head; _head = _head->_next; delete current; } _current_nb_ip = 0; _head = nullptr; return true; } bool AuthenticationService::ClearCurrentSession() { String cookie = _webserver->header("Cookie"); int pos = cookie.indexOf("ESPSESSIONID="); String sessionID; if (pos != -1) { int pos2 = cookie.indexOf(";", pos); sessionID = cookie.substring(pos + strlen("ESPSESSIONID="), pos2); } return ClearAuthIP(_webserver->client().remoteIP(), sessionID.c_str()); } bool AuthenticationService::CreateSession(level_authenticate_type auth_level, const char *username, const char *session_ID) { auth_ip *current_auth = new auth_ip; current_auth->level = auth_level; current_auth->ip = _webserver->client().remoteIP(); strcpy(current_auth->sessionID, session_ID); strcpy(current_auth->userID, username); current_auth->last_time = millis(); #ifndef ALLOW_MULTIPLE_SESSIONS //if not multiple session no need to keep all session, current one is enough ClearAllSessions(); #endif //ALLOW_MULTIPLE_SESSIONS if (AddAuthIP(current_auth)) { return true; } else { delete current_auth; return false; } } bool AuthenticationService::ClearAuthIP(IPAddress ip, const char *sessionID) { auth_ip *current = _head; auth_ip *previous = NULL; bool done = false; while (current) { if ((ip == current->ip) && (strcmp(sessionID, current->sessionID) == 0)) { //remove done = true; if (current == _head) { _head = current->_next; _current_nb_ip--; delete current; current = _head; } else { previous->_next = current->_next; _current_nb_ip--; delete current; current = previous->_next; } } else { previous = current; current = current->_next; } } return done; } //Get info auth_ip *AuthenticationService::GetAuth(IPAddress ip, const char *sessionID) { auth_ip *current = _head; while (current) { if (ip == current->ip) { if (strcmp(sessionID, current->sessionID) == 0) { //found return current; } } //previous = current; current = current->_next; } return NULL; } //Get time left for specific session uint32_t AuthenticationService::getSessionRemaining(const char *sessionID) { auth_ip *current = _head; if ((sessionID == nullptr) || (strlen(sessionID) == 0)) { return 0; } while (current) { if (strcmp(sessionID, current->sessionID) == 0) { //found uint32_t now = millis(); if ((now - current->last_time) > _sessionTimeout) { return 0; } return _sessionTimeout - (now - current->last_time); } //previous = current; current = current->_next; } return 0; } //Review all IP to reset timers level_authenticate_type AuthenticationService::ResetAuthIP(IPAddress ip, const char *sessionID) { auth_ip *current = _head; auth_ip *previous = NULL; //get time //uint32_t now = millis(); while (current) { //if time out is reached and time out is not disabled //if IP is not current one and time out is disabled if ((((millis() - current->last_time) > _sessionTimeout) && (_sessionTimeout != 0)) || ((ip != current->ip) && (_sessionTimeout == 0))) { //remove if (current == _head) { _head = current->_next; _current_nb_ip--; delete current; current = _head; } else { previous->_next = current->_next; _current_nb_ip--; delete current; current = previous->_next; } } else { if (ip == current->ip) { if (strcmp(sessionID, current->sessionID) == 0) { //reset time current->last_time = millis(); return (level_authenticate_type)current->level; } } previous = current; current = current->_next; } } return LEVEL_GUEST; } #endif //HTTP_FEATURE #endif //AUTHENTICATION_FEATURE