Implement user gender handling and enhance gender-based access validation
Added functionality to load user gender from user parameters, improving the accuracy of gender-based access control. Introduced a new `normalize_gender_value` function to standardize gender inputs and updated the `room_access_allowed` function to utilize configured gender IDs for logging and validation. Enhanced debug logging to include configured gender IDs for better troubleshooting. This update ensures more robust handling of user gender in the application.
This commit is contained in:
@@ -1192,14 +1192,17 @@ async fn room_access_allowed(
|
||||
if let Some(required_gender) = room.gender_restriction_id {
|
||||
if required_gender > 0 && user_gender_id != Some(required_gender) {
|
||||
if room_debug_enabled() {
|
||||
let (male_id, female_id) = configured_gender_ids();
|
||||
eprintln!(
|
||||
"[yourchat2][room-debug][access] denied=room_gender_restricted room='{}' required_gender_id={} required_gender='{}' user_gender_id={:?} user_gender='{}' user_falukant_id={:?}",
|
||||
"[yourchat2][room-debug][access] denied=room_gender_restricted room='{}' required_gender_id={} required_gender='{}' user_gender_id={:?} user_gender='{}' user_falukant_id={:?} configured_gender_ids={{male:{},female:{}}}",
|
||||
room.name,
|
||||
required_gender,
|
||||
gender_label_from_id(Some(required_gender)),
|
||||
user_gender_id,
|
||||
gender_label_from_id(user_gender_id),
|
||||
falukant_user_id
|
||||
falukant_user_id,
|
||||
male_id,
|
||||
female_id
|
||||
);
|
||||
}
|
||||
return Err("room_gender_restricted");
|
||||
@@ -1639,9 +1642,10 @@ fn value_as_string(value: &Value) -> Option<String> {
|
||||
}
|
||||
|
||||
fn parse_gender_mf(value: &str) -> Option<Option<i32>> {
|
||||
let (male_id, female_id) = configured_gender_ids();
|
||||
match value.trim().to_lowercase().as_str() {
|
||||
"m" | "male" => Some(Some(1)),
|
||||
"f" | "female" => Some(Some(2)),
|
||||
"m" | "male" => Some(Some(male_id)),
|
||||
"f" | "female" => Some(Some(female_id)),
|
||||
"any" | "none" | "-" => Some(None),
|
||||
_ => None,
|
||||
}
|
||||
@@ -1662,14 +1666,29 @@ fn room_debug_enabled() -> bool {
|
||||
}
|
||||
|
||||
fn gender_label_from_id(gender_id: Option<i32>) -> &'static str {
|
||||
let (male_id, female_id) = configured_gender_ids();
|
||||
match gender_id {
|
||||
Some(1) => "m",
|
||||
Some(2) => "f",
|
||||
Some(v) if v == male_id => "m",
|
||||
Some(v) if v == female_id => "f",
|
||||
Some(_) => "unknown",
|
||||
None => "none",
|
||||
}
|
||||
}
|
||||
|
||||
fn configured_gender_ids() -> (i32, i32) {
|
||||
let male_id = env::var("CHAT_GENDER_MALE_ID")
|
||||
.ok()
|
||||
.and_then(|v| v.trim().parse::<i32>().ok())
|
||||
.filter(|v| *v > 0)
|
||||
.unwrap_or(1);
|
||||
let female_id = env::var("CHAT_GENDER_FEMALE_ID")
|
||||
.ok()
|
||||
.and_then(|v| v.trim().parse::<i32>().ok())
|
||||
.filter(|v| *v > 0)
|
||||
.unwrap_or(2);
|
||||
(male_id, female_id)
|
||||
}
|
||||
|
||||
fn command_from_chat_line(message: &str, token: Option<String>) -> Option<Command> {
|
||||
let trimmed = message.trim();
|
||||
if !trimmed.starts_with('/') {
|
||||
|
||||
95
src/db.rs
95
src/db.rs
@@ -340,6 +340,10 @@ fn decrypt_aes256_ecb_hex(encrypted_hex: &str) -> Option<String> {
|
||||
}
|
||||
|
||||
async fn load_user_gender(client: Arc<PgClient>, user_id: i32) -> Option<i32> {
|
||||
if let Some(mapped) = load_user_gender_from_user_param(client.clone(), user_id).await {
|
||||
return Some(mapped);
|
||||
}
|
||||
|
||||
for query in [
|
||||
"SELECT gender_restriction_id AS gender_id FROM community.\"user\" WHERE id = $1 LIMIT 1",
|
||||
"SELECT gender_type_id AS gender_id FROM community.\"user\" WHERE id = $1 LIMIT 1",
|
||||
@@ -357,6 +361,67 @@ async fn load_user_gender(client: Arc<PgClient>, user_id: i32) -> Option<i32> {
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
for description in ["gender", "sex", "geschlecht"] {
|
||||
if let Ok(Some(row)) = client
|
||||
.query_opt(
|
||||
"SELECT up.value
|
||||
FROM community.user_param up
|
||||
JOIN \"type\".user_param tp ON up.param_type_id = tp.id
|
||||
WHERE up.user_id = $1 AND tp.description = $2
|
||||
LIMIT 1",
|
||||
&[&user_id, &description],
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let Some(value) = row.get::<_, Option<String>>("value") {
|
||||
if let Some(parsed) = normalize_gender_value(&value) {
|
||||
return Some(parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
async fn load_user_gender_from_user_param(client: Arc<PgClient>, user_id: i32) -> Option<i32> {
|
||||
let row = client
|
||||
.query_opt(
|
||||
"SELECT
|
||||
up.value AS raw_value,
|
||||
tpv_id.value AS mapped_by_id_value,
|
||||
tpv_order.value AS mapped_by_order_value
|
||||
FROM community.user_param up
|
||||
JOIN \"type\".user_param tp
|
||||
ON up.param_type_id = tp.id
|
||||
LEFT JOIN \"type\".user_param_value tpv_id
|
||||
ON tpv_id.user_param_type_id = tp.id
|
||||
AND tpv_id.id::text = up.value
|
||||
LEFT JOIN \"type\".user_param_value tpv_order
|
||||
ON tpv_order.user_param_type_id = tp.id
|
||||
AND tpv_order.order_id::text = up.value
|
||||
WHERE up.user_id = $1
|
||||
AND tp.description = 'gender'
|
||||
LIMIT 1",
|
||||
&[&user_id],
|
||||
)
|
||||
.await
|
||||
.ok()??;
|
||||
|
||||
let raw_value = row.get::<_, Option<String>>("raw_value");
|
||||
let mapped_by_id_value = row.get::<_, Option<String>>("mapped_by_id_value");
|
||||
let mapped_by_order_value = row.get::<_, Option<String>>("mapped_by_order_value");
|
||||
|
||||
for candidate in [
|
||||
mapped_by_id_value.as_deref(),
|
||||
mapped_by_order_value.as_deref(),
|
||||
raw_value.as_deref(),
|
||||
] {
|
||||
if let Some(value) = candidate {
|
||||
if let Some(parsed) = normalize_gender_value(value) {
|
||||
return Some(parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -499,3 +564,33 @@ async fn resolve_owner_falukant_id(client: Arc<PgClient>, room_owner_id: i32) ->
|
||||
.ok()??;
|
||||
row.get::<_, Option<i32>>("falukant_user_id")
|
||||
}
|
||||
|
||||
fn normalize_gender_value(raw: &str) -> Option<i32> {
|
||||
let trimmed = raw.trim();
|
||||
if trimmed.is_empty() {
|
||||
return None;
|
||||
}
|
||||
if let Ok(id) = trimmed.parse::<i32>() {
|
||||
return (id > 0).then_some(id);
|
||||
}
|
||||
let (male_id, female_id) = configured_gender_ids();
|
||||
match trimmed.to_lowercase().as_str() {
|
||||
"m" | "male" | "mann" | "männlich" | "maennlich" | "transmale" => Some(male_id),
|
||||
"f" | "female" | "frau" | "weiblich" | "transfemale" => Some(female_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn configured_gender_ids() -> (i32, i32) {
|
||||
let male_id = env::var("CHAT_GENDER_MALE_ID")
|
||||
.ok()
|
||||
.and_then(|v| v.trim().parse::<i32>().ok())
|
||||
.filter(|v| *v > 0)
|
||||
.unwrap_or(1);
|
||||
let female_id = env::var("CHAT_GENDER_FEMALE_ID")
|
||||
.ok()
|
||||
.and_then(|v| v.trim().parse::<i32>().ok())
|
||||
.filter(|v| *v > 0)
|
||||
.unwrap_or(2);
|
||||
(male_id, female_id)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user