mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
Add GET /api/auth/sessions for listing the currently active sessions
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
+2
-1
@@ -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 },
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user