Enhance room access validation and database structure in yourchat2

Updated the room access logic in `handle_init_command` and `handle_join_command` to improve validation against user rights and room ownership. Introduced new fields in the `RoomMeta` structure for room type and friends-only access. Modified database queries to accommodate these changes, ensuring robust access control based on user relationships and room settings.
This commit is contained in:
Torsten Schulz (local)
2026-03-04 22:35:16 +01:00
parent fbbb698ed9
commit 3eaf31d64f
3 changed files with 216 additions and 44 deletions

View File

@@ -1,7 +1,7 @@
use crate::types::{ChatState, ClientId, Command, RoomMeta, ServerConfig};
use bcrypt::verify as bcrypt_verify;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use tokio::sync::RwLock;
use uuid::Uuid;
@@ -148,6 +148,33 @@ async fn handle_init_command(
};
let room_name = normalize_room_name(command.room.as_deref().unwrap_or("lobby"));
let room_password = command.password.clone().unwrap_or_default();
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;
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;
return;
};
(resolved_room_name, room_meta)
};
if !room_access_allowed(
Some(&room_meta),
profile.falukant_user_id,
profile.gender_id,
profile.age,
&profile.right_type_ids,
&profile.rights,
&room_password,
&config,
)
.await
{
send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await;
return;
}
let (token, user_name, actual_room_name) = {
let mut guard = state.write().await;
@@ -161,20 +188,7 @@ async fn handle_init_command(
send_error(client_id, Arc::clone(&state), "loggedin").await;
return;
}
let Some(resolved_room_name) = resolve_room_name(&guard, &room_name) else {
drop(guard);
send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await;
return;
};
if !room_access_allowed(
guard.room_meta.get(&resolved_room_name),
&profile.display_name,
profile.falukant_user_id,
profile.gender_id,
profile.age,
&profile.right_type_ids,
&room_password,
) {
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;
return;
@@ -262,41 +276,54 @@ async fn handle_join_command(
client_id: ClientId,
command: &Command,
state: Arc<RwLock<ChatState>>,
_config: Arc<ServerConfig>,
config: Arc<ServerConfig>,
) {
if !state::authorize(client_id, command, Arc::clone(&state)).await {
return;
}
let room = normalize_room_name(command.room.as_deref().or(command.name.as_deref()).unwrap_or("lobby"));
let password = command.password.clone().unwrap_or_default();
let (from_room, user_name, actual_room_name) = {
let mut guard = state.write().await;
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 {
drop(guard);
send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await;
return;
};
let (name_for_check, falukant_user_id, gender_id, age, right_type_ids) = {
let Some(client) = guard.clients.get(&client_id) else {
return;
};
(
client.user_name.clone(),
client.falukant_user_id,
client.gender_id,
client.age,
client.right_type_ids.clone(),
)
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;
return;
};
if !room_access_allowed(
guard.room_meta.get(&resolved_room),
&name_for_check,
falukant_user_id,
gender_id,
age,
&right_type_ids,
&password,
) {
let Some(client) = guard.clients.get(&client_id) else {
return;
};
(
resolved_room,
room_meta,
client.falukant_user_id,
client.gender_id,
client.age,
client.right_type_ids.clone(),
client.rights.clone(),
)
};
if !room_access_allowed(
Some(&room_meta),
falukant_user_id,
gender_id,
age,
&right_type_ids,
&rights,
&password,
&config,
)
.await
{
send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await;
return;
}
let (from_room, user_name, actual_room_name) = {
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;
return;
@@ -779,18 +806,25 @@ async fn handle_unknown_command(client_id: ClientId, command: &Command, state: A
}
}
fn room_access_allowed(
async fn room_access_allowed(
room_meta: Option<&RoomMeta>,
_user_name: &str,
falukant_user_id: Option<i32>,
user_gender_id: Option<i32>,
age: Option<i32>,
user_right_type_ids: &std::collections::HashSet<i32>,
user_right_type_ids: &HashSet<i32>,
user_rights: &HashSet<String>,
provided_password: &str,
config: &ServerConfig,
) -> bool {
let Some(room) = room_meta else {
return false;
};
let is_admin = user_rights.contains("admin");
let has_required_right = room
.required_user_right_id
.filter(|v| *v > 0)
.map(|v| user_right_type_ids.contains(&v))
.unwrap_or(false);
if let Some(required_gender) = room.gender_restriction_id {
if required_gender > 0 && user_gender_id != Some(required_gender) {
@@ -798,6 +832,19 @@ fn room_access_allowed(
}
}
if !room.is_public {
let Some(owner_id) = room.owner_id else {
return false;
};
let Some(fid) = falukant_user_id else {
return false;
};
let is_owner = db::user_is_room_owner(config, owner_id, fid).await;
if !is_owner && !is_admin && !has_required_right {
return false;
}
}
if let Some(room_password) = &room.password {
if !room_password.is_empty() && !password_matches(room_password, provided_password) {
return false;
@@ -835,6 +882,17 @@ fn room_access_allowed(
return false;
}
}
if room.friends_of_owner_only {
let Some(owner_id) = room.owner_id else {
return false;
};
let Some(fid) = falukant_user_id else {
return false;
};
if !db::is_friend_of_room_owner(config, owner_id, fid).await && !is_admin {
return false;
}
}
true
}