fix(ws-org): enforce current_org_id for teacher-student APIs

Require organization context in teacher/student relation reads and writes, and prevent fallback to cross-organization queries.

Made-with: Cursor
This commit is contained in:
2026-04-27 22:38:59 +08:00
parent c5134c9356
commit 83f657388c
2 changed files with 87 additions and 133 deletions
+67 -133
View File
@@ -16,6 +16,10 @@ fn current_org_id_from_token_str(token_str: &String) -> Option<String> {
.and_then(|decoded| decoded.current_org_id) .and_then(|decoded| decoded.current_org_id)
} }
fn required_current_org_id_from_token_str(token_str: &String) -> anyhow::Result<String> {
current_org_id_from_token_str(token_str).ok_or_else(|| anyhow!("current_org_id is required"))
}
use htycommons::common::{ use htycommons::common::{
current_local_datetime, get_page_and_page_size, get_some_from_query_params, strip_result_vec, current_local_datetime, get_page_and_page_size, get_some_from_query_params, strip_result_vec,
HtyErr, HtyErrCode, HtyResponse, HtyErr, HtyErrCode, HtyResponse,
@@ -3142,6 +3146,7 @@ pub async fn raw_get_unclaimed_students(
host: HtyHostHeader, host: HtyHostHeader,
db_pool: Arc<DbState>, db_pool: Arc<DbState>,
) -> anyhow::Result<Vec<ReqHtyUserWithInfos>> { ) -> anyhow::Result<Vec<ReqHtyUserWithInfos>> {
let current_org_id = required_current_org_id_from_token_str(&root.0)?;
let url = format!("{}/find_users_with_info_by_role/STUDENT", get_uc_url(),); let url = format!("{}/find_users_with_info_by_role/STUDENT", get_uc_url(),);
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let ret_json = client let ret_json = client
@@ -3161,11 +3166,12 @@ pub async fn raw_get_unclaimed_students(
})?; })?;
let mut resp = vec![]; let mut resp = vec![];
for user_with_info in user_with_infos { for user_with_info in user_with_infos {
let exist = TeacherStudent::verify_exist_and_not_deleted_by_student_id( let exist = TeacherStudent::verify_exist_and_not_deleted_by_student_id_in_org(
user_with_info user_with_info
.hty_id .hty_id
.as_ref() .as_ref()
.ok_or_else(|| anyhow!("hty_id is required"))?, .ok_or_else(|| anyhow!("hty_id is required"))?,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?; )?;
if !exist { if !exist {
@@ -3217,21 +3223,13 @@ pub async fn raw_claim_student(
.clone() .clone()
.ok_or_else(|| anyhow!("id_student is required"))?; .ok_or_else(|| anyhow!("id_student is required"))?;
let current_org_id = current_org_id_from_token_str(&(*token).clone()); let current_org_id = required_current_org_id_from_token_str(&(*token).clone())?;
let res_exist = if let Some(org_id) = &current_org_id { let res_exist = TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org( &id_teacher,
&id_teacher, &id_student,
&id_student, &current_org_id,
org_id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), )?;
)?
} else {
TeacherStudent::verify_exist_by_teacher_id_and_student_id(
&id_teacher,
&id_student,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?
};
if res_exist { if res_exist {
return Err(anyhow!(HtyErr { return Err(anyhow!(HtyErr {
code: HtyErrCode::WebErr, code: HtyErrCode::WebErr,
@@ -3244,7 +3242,7 @@ pub async fn raw_claim_student(
id: uuid(), id: uuid(),
status: String::from("Approved"), status: String::from("Approved"),
created_at: Some(current_local_datetime()), created_at: Some(current_local_datetime()),
org_id: current_org_id, org_id: Some(current_org_id),
}; };
let res = TeacherStudent::create( let res = TeacherStudent::create(
&in_record, &in_record,
@@ -3340,42 +3338,25 @@ pub async fn raw_disclaim_student(
.clone() .clone()
.ok_or_else(|| anyhow!("id_student is required"))?; .ok_or_else(|| anyhow!("id_student is required"))?;
let current_org_id = current_org_id_from_token_str(&(*auth).clone()); let current_org_id = required_current_org_id_from_token_str(&(*auth).clone())?;
let res_exist = if let Some(org_id) = &current_org_id { let res_exist = TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org( &id_teacher,
&id_teacher, &id_student,
&id_student, &current_org_id,
org_id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), )?;
)?
} else {
TeacherStudent::verify_exist_by_teacher_id_and_student_id(
&id_teacher,
&id_student,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?
};
if !res_exist { if !res_exist {
return Err(anyhow!(HtyErr { return Err(anyhow!(HtyErr {
code: HtyErrCode::WebErr, code: HtyErrCode::WebErr,
reason: Some("no existing record for this teacher and student".into()), reason: Some("no existing record for this teacher and student".into()),
})); }));
} }
let res = if let Some(org_id) = &current_org_id { let res = TeacherStudent::del_by_teacher_id_and_student_id_in_org(
TeacherStudent::del_by_teacher_id_and_student_id_in_org( &id_teacher,
&id_teacher, &id_student,
&id_student, &current_org_id,
org_id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), )?;
)?
} else {
let record = TeacherStudent::find_by_teacher_id_and_student_id(
&id_teacher,
&id_student,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
TeacherStudent::delete_by_id(&record.id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?
};
Ok(res) Ok(res)
} }
@@ -3579,19 +3560,12 @@ pub async fn raw_find_all_teachers_by_student_id(
student_id: String, student_id: String,
db_pool: Arc<DbState>, db_pool: Arc<DbState>,
) -> anyhow::Result<Vec<ReqHtyUserWithInfos>> { ) -> anyhow::Result<Vec<ReqHtyUserWithInfos>> {
let current_org_id = current_org_id_from_token_str(&root.0); let current_org_id = required_current_org_id_from_token_str(&root.0)?;
let teacher_ids = if let Some(org_id) = &current_org_id { let teacher_ids = TeacherStudent::get_all_teachers_by_student_id_in_org(
TeacherStudent::get_all_teachers_by_student_id_in_org( &student_id,
&student_id, &current_org_id,
org_id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), )?;
)?
} else {
TeacherStudent::get_all_teachers_by_student_id(
&student_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?
};
debug!( debug!(
"raw_find_all_teachers_by_student_id -> teacher_ids {:?}", "raw_find_all_teachers_by_student_id -> teacher_ids {:?}",
&teacher_ids &teacher_ids
@@ -3652,21 +3626,13 @@ pub async fn raw_delete_teacher_student(
let _teacher = find_hty_user_with_info_by_id(&teacher_id, &root).await?; let _teacher = find_hty_user_with_info_by_id(&teacher_id, &root).await?;
let _student = find_hty_user_with_info_by_id(&student_id, &root).await?; let _student = find_hty_user_with_info_by_id(&student_id, &root).await?;
let current_org_id = current_org_id_from_token_str(&root.0); let current_org_id = required_current_org_id_from_token_str(&root.0)?;
let is_exist = if let Some(org_id) = &current_org_id { let is_exist = TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org( &teacher_id,
&teacher_id, &student_id,
&student_id, &current_org_id,
org_id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), )?;
)?
} else {
TeacherStudent::verify_exist_by_teacher_id_and_student_id(
&teacher_id,
&student_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?
};
if !is_exist { if !is_exist {
return Err(anyhow!(HtyErr { return Err(anyhow!(HtyErr {
@@ -3682,20 +3648,12 @@ pub async fn raw_delete_teacher_student(
// )?; // )?;
// record.status = status; // record.status = status;
if let Some(org_id) = &current_org_id { let _ = TeacherStudent::del_by_teacher_id_and_student_id_in_org(
let _ = TeacherStudent::del_by_teacher_id_and_student_id_in_org( &teacher_id,
&teacher_id, &student_id,
&student_id, &current_org_id,
org_id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), )?;
)?;
} else {
let _ = TeacherStudent::del_by_teacher_id_and_student_id(
&teacher_id,
&student_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
}
Ok(()) Ok(())
} }
@@ -3753,41 +3711,25 @@ pub async fn raw_update_teacher_student(
let _teacher = find_hty_user_with_info_by_id(&teacher_id, &root).await?; let _teacher = find_hty_user_with_info_by_id(&teacher_id, &root).await?;
let _student = find_hty_user_with_info_by_id(&student_id, &root).await?; let _student = find_hty_user_with_info_by_id(&student_id, &root).await?;
let current_org_id = current_org_id_from_token_str(&root.0); let current_org_id = required_current_org_id_from_token_str(&root.0)?;
let is_exist = if let Some(org_id) = &current_org_id { let is_exist = TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org( &teacher_id,
&teacher_id, &student_id,
&student_id, &current_org_id,
org_id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), )?;
)?
} else {
TeacherStudent::verify_exist_by_teacher_id_and_student_id(
&teacher_id,
&student_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?
};
if !is_exist { if !is_exist {
return Err(anyhow!(HtyErr { return Err(anyhow!(HtyErr {
code: HtyErrCode::NullErr, code: HtyErrCode::NullErr,
reason: Some("no record for this teacher and student".to_string()), reason: Some("no record for this teacher and student".to_string()),
})); }));
} }
let mut record = if let Some(org_id) = &current_org_id { let mut record = TeacherStudent::find_by_teacher_id_and_student_id_in_org(
TeacherStudent::find_by_teacher_id_and_student_id_in_org( &teacher_id,
&teacher_id, &student_id,
&student_id, &current_org_id,
org_id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), )?;
)?
} else {
TeacherStudent::find_by_teacher_id_and_student_id(
&teacher_id,
&student_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?
};
record.status = status; record.status = status;
let _ = TeacherStudent::update(&record, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; let _ = TeacherStudent::update(&record, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?;
Ok(()) Ok(())
@@ -3834,21 +3776,13 @@ pub async fn raw_find_all_students_by_teacher_id_and_status(
let status = req_teacher_student let status = req_teacher_student
.status .status
.ok_or_else(|| anyhow!("status is required"))?; .ok_or_else(|| anyhow!("status is required"))?;
let current_org_id = current_org_id_from_token_str(&root.0); let current_org_id = required_current_org_id_from_token_str(&root.0)?;
let student_ids = if let Some(org_id) = &current_org_id { let student_ids = TeacherStudent::get_all_students_by_teacher_id_and_status_in_org(
TeacherStudent::get_all_students_by_teacher_id_and_status_in_org( &teacher_id,
&teacher_id, &status,
&status, &current_org_id,
org_id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), )?;
)?
} else {
TeacherStudent::get_all_students_by_teacher_id_and_status(
&teacher_id,
&status,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?
};
debug!( debug!(
"raw_find_all_students_by_teacher_id_and_status -> student_ids {:?}", "raw_find_all_students_by_teacher_id_and_status -> student_ids {:?}",
&student_ids &student_ids
+20
View File
@@ -3087,6 +3087,26 @@ impl TeacherStudent {
}) })
} }
pub fn verify_exist_and_not_deleted_by_student_id_in_org(
id_student: &String,
id_org: &String,
conn: &mut PgConnection,
) -> anyhow::Result<bool> {
use crate::schema::teacher_student::dsl::*;
select(exists(
teacher_student
.filter(student_id.eq(id_student).and(status.ne("Removed")))
.filter(org_id.eq(id_org)),
))
.get_result(conn)
.map_err(|e| {
anyhow!(HtyErr {
code: HtyErrCode::DbErr,
reason: Some(e.to_string()),
})
})
}
pub fn delete_all(conn: &mut PgConnection) -> anyhow::Result<usize> { pub fn delete_all(conn: &mut PgConnection) -> anyhow::Result<usize> {
match diesel::delete(teacher_student::table).execute(conn) { match diesel::delete(teacher_student::table).execute(conn) {
Ok(num) => Ok(num), Ok(num) => Ok(num),