Add GET /api/auth/sessions for listing the currently active sessions

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2023-02-12 20:49:16 +01:00
parent 5e436312f7
commit db0217f6f7
5 changed files with 110 additions and 2 deletions
+2 -1
View File
@@ -35,6 +35,8 @@ static struct {
// domains json fifo
// Note: The order of appearance matters here, more specific URIs have to
// appear *before* less specific URIs: 1. "/a/b/c", 2. "/a/b", 3. "/a"
{ "/api/auth/sessions", "", api_auth_sessions, { false, true, 0 }, true, HTTP_GET },
{ "/api/auth", "", api_auth, { false, true, 0 }, false, HTTP_GET | HTTP_POST | HTTP_DELETE },
{ "/api/dns/blocking", "", api_dns_blocking, { false, true, 0 }, true, HTTP_GET | HTTP_POST },
{ "/api/clients", "/{client}", api_list, { false, true, 0 }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE },
{ "/api/domains", "/{type}/{kind}/{domain}", api_list, { false, true, 0 }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE },
@@ -72,7 +74,6 @@ static struct {
{ "/api/stats/database/summary", "", api_stats_database_summary, { false, true, 0 }, true, HTTP_GET },
{ "/api/stats/database/query_types", "", api_stats_database_query_types, { false, true, 0 }, true, HTTP_GET },
{ "/api/stats/database/upstreams", "", api_stats_database_upstreams, { false, true, 0 }, true, HTTP_GET },
{ "/api/auth", "", api_auth, { false, true, 0 }, false, HTTP_GET | HTTP_POST | HTTP_DELETE },
{ "/api/config", "", api_config, { false, true, 0 }, true, HTTP_GET | HTTP_PATCH },
{ "/api/config", "/{element}", api_config, { false, true, 0 }, true, HTTP_GET | HTTP_PATCH },
{ "/api/config", "/{element}/{value}", api_config, { false, true, 0 }, true, HTTP_DELETE | HTTP_PUT },
+1
View File
@@ -78,6 +78,7 @@ int api_group(struct ftl_conn *api);
int check_client_auth(struct ftl_conn *api);
int api_auth(struct ftl_conn *api);
void delete_all_sessions(void);
int api_auth_sessions(struct ftl_conn *api);
// Documentation methods
int api_docs(struct ftl_conn *api);
+49 -1
View File
@@ -57,8 +57,9 @@ static struct {
bool used;
time_t valid_until;
char remote_addr[48]; // Large enough for IPv4 and IPv6 addresses, hard-coded in civetweb.h as mg_request_info.remote_addr
char user_agent[128];
char sid[SID_SIZE];
} auth_data[API_MAX_CLIENTS] = {{false, 0, {0}, {0}}};
} auth_data[API_MAX_CLIENTS] = {{false, 0, {0}, {0}, {0}}};
#define CHALLENGE_SIZE (2*SHA256_DIGEST_SIZE)
static struct {
@@ -234,6 +235,28 @@ static bool check_response(const char *response, const time_t now)
return false;
}
static int get_all_sessions(struct ftl_conn *api, cJSON *json)
{
const time_t now = time(NULL);
cJSON *sessions = JSON_NEW_ARRAY();
for(unsigned int i = 0; i < API_MAX_CLIENTS; i++)
{
if(auth_data[i].used)
{
cJSON *session = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(session, "id", i);
JSON_ADD_BOOL_TO_OBJECT(session, "valid", auth_data[i].valid_until >= now);
JSON_ADD_NUMBER_TO_OBJECT(session, "last_active", auth_data[i].valid_until - config.webserver.sessionTimeout.v.ui);
JSON_ADD_NUMBER_TO_OBJECT(session, "valid_until", auth_data[i].valid_until);
JSON_REF_STR_IN_OBJECT(session, "remote_addr", auth_data[i].remote_addr);
JSON_REF_STR_IN_OBJECT(session, "user_agent", auth_data[i].user_agent);
JSON_ADD_ITEM_TO_ARRAY(sessions, session);
}
}
JSON_ADD_ITEM_TO_OBJECT(json, "sessions", sessions);
return 0;
}
static int get_session_object(struct ftl_conn *api, cJSON *json, const int user_id, const time_t now)
{
// Authentication not needed
@@ -277,6 +300,7 @@ static void delete_session(const int user_id)
auth_data[user_id].valid_until = 0;
memset(auth_data[user_id].sid, 0, sizeof(auth_data[user_id].sid));
memset(auth_data[user_id].remote_addr, 0, sizeof(auth_data[user_id].remote_addr));
memset(auth_data[user_id].user_agent, 0, sizeof(auth_data[user_id].user_agent));
}
void delete_all_sessions(void)
@@ -502,10 +526,26 @@ int api_auth(struct ftl_conn *api)
// Found unused authentication slot (might have been freed before)
if(!auth_data[i].used)
{
// Mark as used
auth_data[i].used = true;
// Set validitiy to now + timeout
auth_data[i].valid_until = now + config.webserver.sessionTimeout.v.ui;
// Set remote address
strncpy(auth_data[i].remote_addr, api->request->remote_addr, sizeof(auth_data[i].remote_addr));
auth_data[i].remote_addr[sizeof(auth_data[i].remote_addr)-1] = '\0';
// Store user-agent (if available)
const char *user_agent = mg_get_header(api->conn, "user-agent");
if(user_agent != NULL)
{
strncpy(auth_data[i].user_agent, user_agent, sizeof(auth_data[i].user_agent));
auth_data[i].user_agent[sizeof(auth_data[i].user_agent)-1] = '\0';
}
else
{
auth_data[i].user_agent[0] = '\0';
}
// Generate new SID
generateSID(auth_data[i].sid);
user_id = i;
@@ -605,3 +645,11 @@ char * __attribute__((malloc)) hash_password(const char *password)
return strdup(response);
}
int api_auth_sessions(struct ftl_conn *api)
{
// Get session object
cJSON *json = JSON_NEW_OBJECT();
get_all_sessions(api, json);
JSON_SEND_OBJECT(json);
}
+55
View File
@@ -105,6 +105,26 @@ components:
examples:
login_failed:
$ref: 'auth.yaml#/components/examples/login_failed'
sessions_list:
get:
summary: List of all current sessions
tags:
- Authentication
operationId: "get_auth_sessions"
description: List of all current sessions including their validity and further information about the client such as the IP address and user agent.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: 'auth.yaml#/components/schemas/sessions_list'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
schemas:
session:
@@ -143,6 +163,41 @@ components:
type: string
description: Response to previous challenge
example: abcdef
sessions_list:
type: object
description: List of all current sessions
properties:
sessions:
type: array
items:
type: object
description: Session object
properties:
id:
type: number
description: Session ID
valid:
type: boolean
description: Valid session indicator (existing sessions may be invalid due to timeout)
last_active:
type: integer
description: Timestamp of last activity (seconds since epoch)
valid_until:
type: integer
description: Timestamp of session expiration (seconds since epoch)
remote_addr:
type: string
description: IP address of the client
user_agent:
type: string
description: User agent of the client
example:
- id: 1
valid: true
last_active: 1580000000
valid_until: 1580000300
remote_addr: "192.168.0.34"
user_agent: "Mozilla/5.0 (X11; Linux x86_64; rv:107.0) Gecko/20100101 Firefox/107.0"
errors:
bad_request:
+3
View File
@@ -57,6 +57,9 @@ paths:
/auth:
$ref: 'auth.yaml#/components/paths/auth'
/auth/sessions:
$ref: 'auth.yaml#/components/paths/sessions_list'
/stats/summary:
$ref: 'stats.yaml#/components/paths/summary'