From 346f32d3d0d38b6cd9a877a15b379ce466bc6c0d Mon Sep 17 00:00:00 2001 From: Moder Steven Date: Mon, 20 Jul 2026 13:47:15 +0800 Subject: [PATCH] fix(web): scope GET /api/v1/sessions to the authenticated user (#2445) handle_list_all_sessions returned client_mgr.list_sessions(), which iterates user_clients_map across ALL users and returns every session's StorageToken (token, client_url, machine_id, user_id). The handler is mounted under login_required! but performed no per-user authorization: it fetched get_group_permissions() only to println! the result, then returned the full cross-user list. Any authenticated user could read every other user's device token and public client_url. Scope the result to the caller by adding Storage::list_user_client_tokens(user_id) / ClientManager::list_sessions_by_user_id(user_id), mirroring the existing per-user pattern in handle_get_summary (list_machine_by_user_id). Also drop the leftover debug println! and the unwrap() on the current user (return 401 instead). --- easytier-web/src/client_manager/mod.rs | 4 ++++ easytier-web/src/client_manager/storage.rs | 15 +++++++++++++++ easytier-web/src/restful/mod.rs | 13 +++++-------- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/easytier-web/src/client_manager/mod.rs b/easytier-web/src/client_manager/mod.rs index a08e95ac..c0b59337 100644 --- a/easytier-web/src/client_manager/mod.rs +++ b/easytier-web/src/client_manager/mod.rs @@ -161,6 +161,10 @@ impl ClientManager { self.storage.list_clients() } + pub async fn list_sessions_by_user_id(&self, user_id: UserIdInDb) -> Vec { + self.storage.list_user_client_tokens(user_id) + } + pub async fn list_all_sessions(&self) -> Vec { self.storage.list_all_clients() } diff --git a/easytier-web/src/client_manager/storage.rs b/easytier-web/src/client_manager/storage.rs index 297d8666..8d195bb4 100644 --- a/easytier-web/src/client_manager/storage.rs +++ b/easytier-web/src/client_manager/storage.rs @@ -143,6 +143,21 @@ impl Storage { self.list_clients_with_auth(true) } + /// List authorized client sessions that belong to a single user only. + pub fn list_user_client_tokens(&self, user_id: UserIdInDb) -> Vec { + self.0 + .user_clients_map + .get(&user_id) + .map(|info_map| { + info_map + .iter() + .filter(|info| info.value().authorized) + .map(|info| info.value().storage_token.clone()) + .collect() + }) + .unwrap_or_default() + } + pub fn list_all_clients(&self) -> Vec { self.list_clients_with_auth(false) } diff --git a/easytier-web/src/restful/mod.rs b/easytier-web/src/restful/mod.rs index f759e067..cf449d9b 100644 --- a/easytier-web/src/restful/mod.rs +++ b/easytier-web/src/restful/mod.rs @@ -14,7 +14,7 @@ use axum::response::Response; use axum::routing::{delete, post}; use axum::{Extension, Json, Router, extract::State, routing::get}; use axum_login::tower_sessions::{ExpiredDeletion, SessionManagerLayer}; -use axum_login::{AuthManagerLayerBuilder, AuthUser, AuthzBackend, login_required}; +use axum_login::{AuthManagerLayerBuilder, AuthUser, login_required}; use axum_messages::MessagesManagerLayer; use easytier::common::config::{ConfigLoader, TomlConfigLoader}; use easytier::launcher::NetworkConfig; @@ -131,13 +131,10 @@ impl RestfulServer { auth_session: AuthSession, State(client_mgr): AppState, ) -> Result, HttpHandleError> { - let perms = auth_session - .backend - .get_group_permissions(auth_session.user.as_ref().unwrap()) - .await - .unwrap(); - println!("{:?}", perms); - let ret = client_mgr.list_sessions().await; + let Some(user) = auth_session.user else { + return Err((StatusCode::UNAUTHORIZED, other_error("No such user").into())); + }; + let ret = client_mgr.list_sessions_by_user_id(user.id()).await; Ok(ListSessionJsonResp(ret).into()) }