Skip to content

Commit

Permalink
Move to new messaging API for authentication- WIP
Browse files Browse the repository at this point in the history
Fix compilation issue when enabling AUTHENTICATION_FEATURE
Add doc for Authentication - WIP
  • Loading branch information
luc-github committed Nov 29, 2023
1 parent cb6b47a commit fb283ac
Show file tree
Hide file tree
Showing 21 changed files with 141 additions and 53 deletions.
75 changes: 75 additions & 0 deletions docs/Files/authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
+++
archetype = "section"
title = "Authentication"
description = "What is authentication in ESP3D?"
weight = 800
+++

# Definition
The authentication is an additional security layer to protect the ESP3D web interface and ESP3D commands from unauthorized access. It is based on a username and a password. The authentication is optional and can be enabled/disabled in the ESP3D configuration. There are 3 login levels for authentication:
- guest, which is does not need any authentication
- user, which has limited access to ESP3D features
- admin, which has full access to ESP3D features

Currently the login cannot be customized and so is limited to `user` and `admin` levels. The `guest` level is always available and cannot be disabled.

# Configuration

In configuration.h just uncomment the following line to enable the authentication:
```c++
#define AUTHENTICATION_FEATURE
```
Default password authentication for `admin` is `admin` and for 'user' is `user`. You can change them using WebInterface or [ESP550] and [ESP555] commands.

# Usage

## Web Interface

When user authentication is enabled, the web interface will ask for a username and a password. If the authentication is successful, the user will be redirected to the web interface. If the authentication fails, the user will be redirected to the login page.

The web interface allows also inline authentication. This means that you can pass the username and password in the URL. This is useful if you want to use some command line to access the web interface like curl or wget. The URL format is the following:

```html
http://user:password@<ip_address>
```

On the web interface an authenticated session will stay open until the browser is closed. So if you close the browser and reopen it, you will be asked for authentication again. This session can also have a timeout. The default timeout is 3 minutes of inactivity. This timeout can be changed in the ESP3D configuration web interface or using `[ESP510]` command.

## ESPXXX Command

When user authentication is enabled, the ESPXXX commands will ask for a password. If the authentication is successful, the command will be executed. If the authentication fails, the command will be rejected.

The session for ESPXXX commands is a sticky session. This means that once authenticated, the session will stay authenticated until the ESP3D is restarted or session is closed (e.g: Telnet / WebSocket).
# Limitations

The current authentication miss a lot of features, like:
- user management
- https support
- password encryption
- password recovery
- password expiration in time
- password lockout if too many failed attempts

So you must not consider authentication as fullproof for security. It is just an additional layer of security.

Because ESPXXX commands only rely on password, do not use same password for user and admin users. If you do so, you will not be able to use ESPXXX commands with user level, everything will be considered as admin when authenticated.

The password are never been displayed in clear text, but they are stored in the ESP3D configuration in clear text. So if you want to change the password, you must use the WebInterface or ESPXXX commands.
In web interface the passwords are replaced by `*******` so any modification must be complete not partial.

All passwords and sensitive informations are sent using plain text. So if you want to use ESP3D in a public network or outside of your local network (which is not recommended), you must use a VPN.

# API Description

## Global
Each authenticated session have unique session id, that will be stored on ESP3D with additionnal informations:
- session id (25 characters)
- session level (Guest / Admin / User)
- client_id (serial / http / telnet / WebSocket)
- session last activity (timestamp)
- client IP (http)
- Client socket ID (telnet / WebSocket)

## Http
When authentication is enabled, the http server will check if the session is authenticated. If not, it will ask for authentication. If the session is authenticated, it will check if the session is still valid. If not, it will ask for authentication again. If the session is still valid, it will process the request.
the Session ID is stored in the cookie `ESP3D_SESSIONID`.
2 changes: 1 addition & 1 deletion esp3d/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@
/* Enable authentication
* Force usage of authentication for commands
*/
// #define AUTHENTICATION_FEATURE
#define AUTHENTICATION_FEATURE

/************************************
*
Expand Down
2 changes: 1 addition & 1 deletion esp3d/src/core/commands/ESP160.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void ESP3DCommands::ESP160(int cmd_params_pos, ESP3DMessage* msg) {
bool stateOFF = hasTag(msg, cmd_params_pos, "OFF");
bool has_param = false;
String tmpstr;
#if AUTHENTICATION_FEATURE
#ifdef AUTHENTICATION_FEATURE
if (msg->authentication_level == ESP3DAuthenticationLevel::guest) {
msg->authentication_level = ESP3DAuthenticationLevel::not_authenticated;
dispatchAuthenticationError(msg, COMMAND_ID, json);
Expand Down
2 changes: 1 addition & 1 deletion esp3d/src/core/commands/ESP190.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void ESP3DCommands::ESP190(int cmd_params_pos, ESP3DMessage* msg) {
bool stateOFF = hasTag(msg, cmd_params_pos, "OFF");
bool has_param = false;
String tmpstr;
#if AUTHENTICATION_FEATURE
#ifdef AUTHENTICATION_FEATURE
if (msg->authentication_level == ESP3DAuthenticationLevel::guest) {
msg->authentication_level = ESP3DAuthenticationLevel::not_authenticated;
dispatchAuthenticationError(msg, COMMAND_ID, json);
Expand Down
11 changes: 8 additions & 3 deletions esp3d/src/core/commands/ESP420.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@
#include "../../modules/display/display.h"
#endif // DISPLAY_DEVICE
#define COMMAND_ID 420
#if defined(AUTHENTICATION_FEATURE)
#include "../../modules/authentication/authentication_service.h"
#endif // AUTHENTICATION_FEATURE

// Get ESP current status
// output is JSON or plain text according parameter
Expand Down Expand Up @@ -269,7 +272,6 @@ void ESP3DCommands::ESP420(int cmd_params_pos, ESP3DMessage* msg) {
requestId, false)) {
return;
}

}

#endif // TELNET_FEATURE
Expand Down Expand Up @@ -595,12 +597,15 @@ void ESP3DCommands::ESP420(int cmd_params_pos, ESP3DMessage* msg) {
// MKS_SERIAL
#if defined(AUTHENTICATION_FEATURE)
// authentication enabled
tmpstr = (authentication_service.started()) ? "ON" : "OFF";
tmpstr = "ON";
#else
tmpstr = "OFF";
#endif // AUTHENTICATION_FEATURE
if (!dispatchIdValue(json, "authentication", tmpstr.c_str(), target,
requestId, false)) {
return;
}
#endif // AUTHENTICATION_FEATURE

#if defined(NOTIFICATION_FEATURE)
// notification enabled
tmpstr = (notificationsservice.started()) ? "ON" : "OFF";
Expand Down
8 changes: 4 additions & 4 deletions esp3d/src/core/esp3d_commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,10 +295,10 @@ void ESP3DCommands::execute_internal_command(int cmd, int cmd_params_pos,
#ifdef AUTHENTICATION_FEATURE

// do not overwrite previous authetic <time=YYYY-MM-DD#H24:MM:SS>ation level
if (auth_type == guest) {
String pwd = get_param(cmd_params, "pwd=");
auth_type =
AuthenticationService::authenticated_level(pwd.c_str(), esp3dmsg);
if (msg->authentication_level == ESP3DAuthenticationLevel::guest) {
String pwd = get_param(msg, cmd_params_pos, "pwd=");
msg->authentication_level =
AuthenticationService::getAuthenticatedLevel(pwd.c_str(), msg);
}
#endif // AUTHENTICATION_FEATURE
switch (cmd) {
Expand Down
36 changes: 20 additions & 16 deletions esp3d/src/modules/authentication/authentication_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,21 @@ uint8_t AuthenticationService::_current_nb_ip = 0;
// #define ALLOW_MULTIPLE_SESSIONS

// check authentification
ESP3DAuthenticationLevel AuthenticationService::authenticated_level(
ESP3DAuthenticationLevel AuthenticationService::getAuthenticatedLevel(
const char *pwd, ESP3DMessage *esp3dmsg) {
#ifdef AUTHENTICATION_FEATURE
ESP3DAuthenticationLevel auth_type = guest;
ESP3DAuthenticationLevel auth_type = ESP3DAuthenticationLevel::guest;
if (pwd != nullptr) {
if (isadmin(pwd)) {
auth_type = admin;
auth_type = ESP3DAuthenticationLevel::admin;
}
if (isuser(pwd) && (auth_type != admin)) {
auth_type = user;
if (isuser(pwd) && (auth_type != ESP3DAuthenticationLevel::admin)) {
auth_type = ESP3DAuthenticationLevel::user;
}
return auth_type;
} else {
if (esp3dmsg) {
if (esp3dmsg->getTarget() != ESP_HTTP_CLIENT) {
if (esp3dmsg->origin != ESP3DClientType::http) {
return auth_type;
}
}
Expand All @@ -72,10 +72,10 @@ ESP3DAuthenticationLevel AuthenticationService::authenticated_level(
if (_webserver->hasHeader("Authorization")) {
// esp3d_log("Check authorization %",(_webserver->uri()).c_str());
if (_webserver->authenticate(DEFAULT_ADMIN_LOGIN, _adminpwd.c_str())) {
auth_type = admin;
auth_type = ESP3DAuthenticationLevel::admin;
} else {
if (_webserver->authenticate(DEFAULT_USER_LOGIN, _userpwd.c_str())) {
auth_type = user;
auth_type = ESP3DAuthenticationLevel::user;
}
}
}
Expand All @@ -87,7 +87,7 @@ ESP3DAuthenticationLevel AuthenticationService::authenticated_level(
int pos2 = cookie.indexOf(";", pos);
String sessionID =
cookie.substring(pos + strlen("ESPSESSIONID="), pos2);
IPAddress ip = _webserver->getTarget().remoteIP();
IPAddress ip = _webserver->client().remoteIP();
// check if cookie can be reset and clean table in same time
auth_type = ResetAuthIP(ip, sessionID.c_str());
// esp3d_log("Authentication = %d", auth_type);
Expand Down Expand Up @@ -197,7 +197,7 @@ bool AuthenticationService::ClearAllSessions() {
while (_head) {
auth_ip *current = _head;
_head = _head->_next;
delete current;
free(current);
}
_current_nb_ip = 0;
_head = nullptr;
Expand All @@ -219,7 +219,11 @@ bool AuthenticationService::ClearCurrentSession() {
bool AuthenticationService::CreateSession(ESP3DAuthenticationLevel auth_level,
const char *username,
const char *session_ID) {
auth_ip *current_auth = new auth_ip;
auth_ip *current_auth = (auth_ip *)malloc(sizeof(auth_ip));
if (!current_auth) {
esp3d_log_e("Error allocating memory for session");
return false;
}
current_auth->level = auth_level;
current_auth->ip = _webserver->client().remoteIP();
strcpy(current_auth->sessionID, session_ID);
Expand All @@ -232,7 +236,7 @@ bool AuthenticationService::CreateSession(ESP3DAuthenticationLevel auth_level,
if (AddAuthIP(current_auth)) {
return true;
} else {
delete current_auth;
free(current_auth);
return false;
}
}
Expand All @@ -248,12 +252,12 @@ bool AuthenticationService::ClearAuthIP(IPAddress ip, const char *sessionID) {
if (current == _head) {
_head = current->_next;
_current_nb_ip--;
delete current;
free(current);
current = _head;
} else {
previous->_next = current->_next;
_current_nb_ip--;
delete current;
free(current);
current = previous->_next;
}
} else {
Expand Down Expand Up @@ -318,12 +322,12 @@ ESP3DAuthenticationLevel AuthenticationService::ResetAuthIP(
if (current == _head) {
_head = current->_next;
_current_nb_ip--;
delete current;
free(current);
current = _head;
} else {
previous->_next = current->_next;
_current_nb_ip--;
delete current;
free(current);
current = previous->_next;
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion esp3d/src/modules/authentication/authentication_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ typedef void Authwebserver;
#endif // AUTHENTICATION_FEATURE
class AuthenticationService {
public:
static ESP3DAuthenticationLevel authenticated_level(
static ESP3DAuthenticationLevel getAuthenticatedLevel(
const char *pwd = nullptr, ESP3DMessage *esp3dmsg = nullptr);
#ifdef AUTHENTICATION_FEATURE
static bool begin(Authwebserver *webserver);
Expand Down
4 changes: 2 additions & 2 deletions esp3d/src/modules/http/handlers/handle-SD-files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
// SD files list and file commands
void HTTP_Server::handleSDFileList() {
ESP3DAuthenticationLevel auth_level =
AuthenticationService::authenticated_level();
AuthenticationService::getAuthenticatedLevel();
HTTP_Server::set_http_headers();

if (auth_level == guest) {
if (auth_level == ESP3DAuthenticationLevel::guest) {
_upload_status = UPLOAD_STATUS_NONE;
_webserver->send(401, "text/plain", "Wrong authentication!");
return;
Expand Down
4 changes: 2 additions & 2 deletions esp3d/src/modules/http/handlers/handle-command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ bool isRealTimeCommand(unsigned char c) {
// Handle web command query and send answer//////////////////////////////
void HTTP_Server::handle_web_command() {
ESP3DAuthenticationLevel auth_level =
AuthenticationService::authenticated_level();
if (auth_level == guest) {
AuthenticationService::getAuthenticatedLevel();
if (auth_level == ESP3DAuthenticationLevel::guest) {
_webserver->send(401, "text/plain", "Wrong authentication!");
return;
}
Expand Down
2 changes: 1 addition & 1 deletion esp3d/src/modules/http/handlers/handle-config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
// answer//////////////////////////////
void HTTP_Server::handle_config() {
ESP3DAuthenticationLevel auth_level =
AuthenticationService::authenticated_level();
AuthenticationService::getAuthenticatedLevel();
String cmd = "[ESP420]addPreTag";
if (_webserver->hasArg("json")) {
cmd += " json=" + _webserver->arg("json");
Expand Down
3 changes: 2 additions & 1 deletion esp3d/src/modules/http/handlers/handle-filenotfound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
void HTTP_Server::handle_not_found() {
HTTP_Server::set_http_headers();

if (AuthenticationService::authenticated_level() == guest) {
if (AuthenticationService::getAuthenticatedLevel() ==
ESP3DAuthenticationLevel::guest) {
_webserver->send(401, "text/plain", "Wrong authentication!");
return;
}
Expand Down
4 changes: 2 additions & 2 deletions esp3d/src/modules/http/handlers/handle-files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ void HTTP_Server::handleFSFileList() {
HTTP_Server::set_http_headers();

ESP3DAuthenticationLevel auth_level =
AuthenticationService::authenticated_level();
if (auth_level == guest) {
AuthenticationService::getAuthenticatedLevel();
if (auth_level == ESP3DAuthenticationLevel::guest) {
_upload_status = UPLOAD_STATUS_NONE;
_webserver->send(401, "text/plain", "Wrong authentication!");
return;
Expand Down
15 changes: 9 additions & 6 deletions esp3d/src/modules/http/handlers/handle-login.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void HTTP_Server::handle_login() {
return;
}
ESP3DAuthenticationLevel auth_level =
AuthenticationService::authenticated_level();
AuthenticationService::getAuthenticatedLevel();
// check is it is a submission or a query
if (_webserver->hasArg("SUBMIT")) {
// is there a correct list of query?
Expand Down Expand Up @@ -87,8 +87,10 @@ void HTTP_Server::handle_login() {
AuthenticationService::setSessionTimeout(timeout.toInt());
}
// it is a change or same level
if (((auth_level == user) && (sUser == DEFAULT_USER_LOGIN)) ||
((auth_level == admin) && (sUser == DEFAULT_ADMIN_LOGIN))) {
if (((auth_level == ESP3DAuthenticationLevel::user) &&
(sUser == DEFAULT_USER_LOGIN)) ||
((auth_level == ESP3DAuthenticationLevel::admin) &&
(sUser == DEFAULT_ADMIN_LOGIN))) {
code = 200;
status = "ok";
} else { // new authentication
Expand All @@ -108,7 +110,8 @@ void HTTP_Server::handle_login() {
}
}
} else {
if (auth_level == user || auth_level == admin) {
if (auth_level == ESP3DAuthenticationLevel::user ||
auth_level == ESP3DAuthenticationLevel::admin) {
status = "Identified";
code = 200;
}
Expand All @@ -117,9 +120,9 @@ void HTTP_Server::handle_login() {
String smsg = "{\"status\":\"";
smsg += status;
smsg += "\",\"authentication_lvl\":\"";
if (auth_level == user) {
if (auth_level == ESP3DAuthenticationLevel::user) {
smsg += "user";
} else if (auth_level == admin) {
} else if (auth_level == ESP3DAuthenticationLevel::admin) {
smsg += "admin";
} else {
smsg += "guest";
Expand Down
4 changes: 2 additions & 2 deletions esp3d/src/modules/http/handlers/handle-mks-files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@

void HTTP_Server::handleMKSUpload() {
ESP3DAuthenticationLevel auth_level =
AuthenticationService::authenticated_level();
if (auth_level == guest) {
AuthenticationService::getAuthenticatedLevel();
if (auth_level == ESP3DAuthenticationLevel::guest) {
_upload_status = UPLOAD_STATUS_NONE;
_webserver->send(401, "text/plain", "Wrong authentication!");
return;
Expand Down
4 changes: 2 additions & 2 deletions esp3d/src/modules/http/handlers/handle-snap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@

void HTTP_Server::handle_snap() {
ESP3DAuthenticationLevel auth_level =
AuthenticationService::authenticated_level();
if (auth_level == guest) {
AuthenticationService::getAuthenticatedLevel();
if (auth_level == ESP3DAuthenticationLevel::guest) {
_webserver->send(401, "text/plain", "Wrong authentication!");
return;
}
Expand Down
Loading

0 comments on commit fb283ac

Please sign in to comment.