From 78aca25722f571227d95ceb8a14e6b969151a1d1 Mon Sep 17 00:00:00 2001 From: mofeng Date: Thu, 29 Jan 2026 20:16:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=83=A8=E5=88=86?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=9C=AA=E6=8E=88=E6=9D=83=E8=AE=BF=E9=97=AE?= =?UTF-8?q?=EF=BC=8C=E5=88=A0=E9=99=A4=E5=86=97=E4=BD=99=20Admin=20?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/middleware.rs | 31 ++++++++++++----------- src/web/handlers/mod.rs | 2 -- src/web/routes.rs | 13 +++------- web/src/api/index.ts | 2 +- web/src/components/ActionBar.vue | 22 +++++++--------- web/src/components/AudioConfigPopover.vue | 9 +++---- web/src/components/HidConfigPopover.vue | 11 +++----- web/src/components/VideoConfigPopover.vue | 13 +++------- web/src/stores/auth.ts | 12 --------- web/src/views/ConsoleView.vue | 5 ++-- 10 files changed, 43 insertions(+), 77 deletions(-) diff --git a/src/auth/middleware.rs b/src/auth/middleware.rs index 2f8daee8..5bbbd2f0 100644 --- a/src/auth/middleware.rs +++ b/src/auth/middleware.rs @@ -40,20 +40,20 @@ pub async fn auth_middleware( mut request: Request, next: Next, ) -> Result { + let raw_path = request.uri().path(); + // When this middleware is mounted under /api, Axum strips the prefix for the inner router. + // Normalize the path so checks work whether it is mounted or not. + let path = raw_path.strip_prefix("/api").unwrap_or(raw_path); + // Check if system is initialized if !state.config.is_initialized() { - // Allow access to setup endpoints when not initialized - let path = request.uri().path(); - if path.starts_with("/api/setup") - || path == "/api/info" - || path.starts_with("/") && !path.starts_with("/api/") - { + // Allow only setup-related endpoints when not initialized + if is_setup_public_endpoint(path) { return Ok(next.run(request).await); } } // Public endpoints that don't require auth - let path = request.uri().path(); if is_public_endpoint(path) { return Ok(next.run(request).await); } @@ -89,21 +89,14 @@ fn unauthorized_response(message: &str) -> Response { /// Check if endpoint is public (no auth required) fn is_public_endpoint(path: &str) -> bool { - // Note: paths here are relative to /api since middleware is applied before nest + // Note: paths here are relative to /api since middleware is applied within the nested router matches!( path, "/" | "/auth/login" - | "/info" | "/health" | "/setup" | "/setup/init" - // Also check with /api prefix for direct access - | "/api/auth/login" - | "/api/info" - | "/api/health" - | "/api/setup" - | "/api/setup/init" ) || path.starts_with("/assets/") || path.starts_with("/static/") || path.ends_with(".js") @@ -112,3 +105,11 @@ fn is_public_endpoint(path: &str) -> bool { || path.ends_with(".png") || path.ends_with(".svg") } + +/// Setup-only endpoints allowed before initialization. +fn is_setup_public_endpoint(path: &str) -> bool { + matches!( + path, + "/setup" | "/setup/init" | "/devices" | "/stream/codecs" + ) +} diff --git a/src/web/handlers/mod.rs b/src/web/handlers/mod.rs index 5e8eb036..77ddc52b 100644 --- a/src/web/handlers/mod.rs +++ b/src/web/handlers/mod.rs @@ -465,7 +465,6 @@ pub async fn logout( pub struct AuthCheckResponse { pub authenticated: bool, pub user: Option, - pub is_admin: bool, } pub async fn auth_check( @@ -481,7 +480,6 @@ pub async fn auth_check( Json(AuthCheckResponse { authenticated: true, user: username, - is_admin: true, }) } diff --git a/src/web/routes.rs b/src/web/routes.rs index f8c6a91b..02e74bf5 100644 --- a/src/web/routes.rs +++ b/src/web/routes.rs @@ -32,7 +32,7 @@ pub fn create_router(state: Arc) -> Router { .route("/setup", get(handlers::setup_status)) .route("/setup/init", post(handlers::setup_init)); - // User routes (authenticated users - both regular and admin) + // Authenticated routes (all logged-in users) let user_routes = Router::new() .route("/info", get(handlers::system_info)) .route("/auth/logout", post(handlers::logout)) @@ -71,10 +71,6 @@ pub fn create_router(state: Arc) -> Router { .route("/audio/devices", get(handlers::list_audio_devices)) // Audio WebSocket endpoint .route("/ws/audio", any(audio_ws_handler)) - ; - - // Admin-only routes (require admin privileges) - let admin_routes = Router::new() // Configuration management (domain-separated endpoints) .route("/config", get(handlers::config::get_all_config)) .route("/config", post(handlers::update_config)) @@ -199,11 +195,10 @@ pub fn create_router(state: Arc) -> Router { .route("/terminal", get(handlers::terminal::terminal_index)) .route("/terminal/", get(handlers::terminal::terminal_index)) .route("/terminal/ws", get(handlers::terminal::terminal_ws)) - .route("/terminal/{*path}", get(handlers::terminal::terminal_proxy)) - ; + .route("/terminal/{*path}", get(handlers::terminal::terminal_proxy)); - // Combine protected routes (user + admin) - let protected_routes = Router::new().merge(user_routes).merge(admin_routes); + // Protected routes (all authenticated users) + let protected_routes = user_routes; // Stream endpoints (accessible with auth, but typically embedded in pages) let stream_routes = Router::new() diff --git a/web/src/api/index.ts b/web/src/api/index.ts index 8ee2459e..5cc503ae 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -16,7 +16,7 @@ export const authApi = { request<{ success: boolean }>('/auth/logout', { method: 'POST' }), check: () => - request<{ authenticated: boolean; user?: string; is_admin?: boolean }>('/auth/check'), + request<{ authenticated: boolean; user?: string }>('/auth/check'), changePassword: (currentPassword: string, newPassword: string) => request<{ success: boolean }>('/auth/password', { diff --git a/web/src/components/ActionBar.vue b/web/src/components/ActionBar.vue index d600f52b..8aa0e268 100644 --- a/web/src/components/ActionBar.vue +++ b/web/src/components/ActionBar.vue @@ -52,14 +52,13 @@ const overflowMenuOpen = ref(false) const hidBackend = computed(() => (systemStore.hid?.backend ?? '').toLowerCase()) const isCh9329Backend = computed(() => hidBackend.value.includes('ch9329')) const showMsd = computed(() => { - return props.isAdmin && !isCh9329Backend.value + return !!systemStore.msd?.available && !isCh9329Backend.value }) const props = defineProps<{ mouseMode?: 'absolute' | 'relative' videoMode?: VideoMode ttydRunning?: boolean - isAdmin?: boolean }>() const emit = defineEmits<{ @@ -93,18 +92,16 @@ const extensionOpen = ref(false) - + @@ -125,7 +122,7 @@ const extensionOpen = ref(false) -