From 8285466ba214860b1fce063b82cc6631fa3c2442 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 4 Mar 2026 23:30:26 +0100 Subject: [PATCH] Enhance room access validation and debugging in command handling Improved error handling in `handle_init_command` and `handle_join_command` to provide clearer feedback when room resolution fails. Added detailed debug logging for room access attempts, including client ID and requested room information, when debugging is enabled. Introduced a new function, `room_debug_enabled`, to toggle debug logging based on environment variables. Updated password validation logic to require a password when one is set for a room. --- src/commands.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 8 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 8d18dd6..a0330db 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -2,6 +2,7 @@ use crate::types::{ChatState, ClientId, Command, RoomMeta, ServerConfig}; use bcrypt::verify as bcrypt_verify; use serde_json::{json, Value}; use std::collections::{HashMap, HashSet}; +use std::env; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; use tokio::sync::RwLock; @@ -160,11 +161,22 @@ async fn handle_init_command( let (resolved_room_name, room_meta) = { let guard = state.read().await; let Some(resolved_room_name) = resolve_room_name(&guard, &room_name) else { - send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await; + if room_debug_enabled() { + let known_rooms = guard.room_meta.keys().cloned().collect::>(); + eprintln!( + "[yourchat2][room-debug][init] client_id={client_id} requested_room='{room_name}' resolve_failed known_rooms={known_rooms:?}" + ); + } + send_error(client_id, Arc::clone(&state), "room_not_found").await; return; }; let Some(room_meta) = guard.room_meta.get(&resolved_room_name).cloned() else { - send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await; + if room_debug_enabled() { + eprintln!( + "[yourchat2][room-debug][init] client_id={client_id} requested_room='{room_name}' resolved_room='{resolved_room_name}' missing_meta" + ); + } + send_error(client_id, Arc::clone(&state), "room_not_found").await; return; }; (resolved_room_name, room_meta) @@ -181,6 +193,20 @@ async fn handle_init_command( ) .await { + if room_debug_enabled() { + eprintln!( + "[yourchat2][room-debug][init] client_id={client_id} user='{}' room='{}' denied='{}' user_profile={{fid:{:?},gender:{:?},age:{:?},rights:{:?},right_ids:{:?}}} room_meta={:?}", + profile.display_name, + resolved_room_name, + access_error, + profile.falukant_user_id, + profile.gender_id, + profile.age, + profile.rights, + profile.right_type_ids, + room_meta + ); + } send_error(client_id, Arc::clone(&state), access_error).await; return; } @@ -199,7 +225,12 @@ async fn handle_init_command( } if !guard.room_meta.contains_key(&resolved_room_name) { drop(guard); - send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await; + if room_debug_enabled() { + eprintln!( + "[yourchat2][room-debug][init] client_id={client_id} resolved_room='{resolved_room_name}' vanished_before_join" + ); + } + send_error(client_id, Arc::clone(&state), "room_not_found").await; return; } @@ -299,11 +330,22 @@ async fn handle_join_command( let (resolved_room, room_meta, falukant_user_id, gender_id, age, right_type_ids, rights) = { let guard = state.read().await; let Some(resolved_room) = resolve_room_name(&guard, &room) else { - send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await; + if room_debug_enabled() { + let known_rooms = guard.room_meta.keys().cloned().collect::>(); + eprintln!( + "[yourchat2][room-debug][join] client_id={client_id} requested_room='{room}' resolve_failed known_rooms={known_rooms:?}" + ); + } + send_error(client_id, Arc::clone(&state), "room_not_found").await; return; }; let Some(room_meta) = guard.room_meta.get(&resolved_room).cloned() else { - send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await; + if room_debug_enabled() { + eprintln!( + "[yourchat2][room-debug][join] client_id={client_id} requested_room='{room}' resolved_room='{resolved_room}' missing_meta" + ); + } + send_error(client_id, Arc::clone(&state), "room_not_found").await; return; }; let Some(client) = guard.clients.get(&client_id) else { @@ -331,6 +373,19 @@ async fn handle_join_command( ) .await { + if room_debug_enabled() { + eprintln!( + "[yourchat2][room-debug][join] client_id={client_id} room='{}' denied='{}' user={{fid:{:?},gender:{:?},age:{:?},rights:{:?},right_ids:{:?}}} room_meta={:?}", + resolved_room, + access_error, + falukant_user_id, + gender_id, + age, + rights, + right_type_ids, + room_meta + ); + } send_error(client_id, Arc::clone(&state), access_error).await; return; } @@ -338,7 +393,12 @@ async fn handle_join_command( let mut guard = state.write().await; if !guard.room_meta.contains_key(&resolved_room) { drop(guard); - send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await; + if room_debug_enabled() { + eprintln!( + "[yourchat2][room-debug][join] client_id={client_id} resolved_room='{resolved_room}' vanished_before_join" + ); + } + send_error(client_id, Arc::clone(&state), "room_not_found").await; return; } let Some(client) = guard.clients.get_mut(&client_id) else { @@ -883,6 +943,12 @@ async fn handle_create_room_command( empty_since_unix: Some(now_unix()), }, ); + if room_debug_enabled() { + eprintln!( + "[yourchat2][room-debug][create] client_id={client_id} room='{}' owner_chat_user_id={} created", + parsed.room_name, owner_chat_user_id + ); + } } state::send_room_list(client_id, Arc::clone(&state)).await; state::send_to_client( @@ -1133,8 +1199,13 @@ async fn room_access_allowed( } if let Some(room_password) = &room.password { - if !room_password.is_empty() && !password_matches(room_password, provided_password) { - return Err("room_password_invalid"); + if !room_password.is_empty() { + if provided_password.trim().is_empty() { + return Err("room_password_required"); + } + if !password_matches(room_password, provided_password) { + return Err("room_password_invalid"); + } } } @@ -1361,6 +1432,13 @@ fn now_unix() -> i64 { .unwrap_or(0) } +fn room_debug_enabled() -> bool { + matches!( + env::var("CHAT_DEBUG_ROOM_ACCESS").ok().as_deref(), + Some("1") | Some("true") | Some("TRUE") | Some("yes") | Some("YES") | Some("on") | Some("ON") + ) +} + fn command_from_chat_line(message: &str, token: Option) -> Option { let trimmed = message.trim(); if !trimmed.starts_with('/') {