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)
}
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::{
current_local_datetime, get_page_and_page_size, get_some_from_query_params, strip_result_vec,
HtyErr, HtyErrCode, HtyResponse,
@@ -3142,6 +3146,7 @@ pub async fn raw_get_unclaimed_students(
host: HtyHostHeader,
db_pool: Arc<DbState>,
) -> 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 client = reqwest::Client::new();
let ret_json = client
@@ -3161,11 +3166,12 @@ pub async fn raw_get_unclaimed_students(
})?;
let mut resp = vec![];
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
.hty_id
.as_ref()
.ok_or_else(|| anyhow!("hty_id is required"))?,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
if !exist {
@@ -3217,21 +3223,13 @@ pub async fn raw_claim_student(
.clone()
.ok_or_else(|| anyhow!("id_student is required"))?;
let current_org_id = current_org_id_from_token_str(&(*token).clone());
let res_exist = if let Some(org_id) = &current_org_id {
TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
&id_teacher,
&id_student,
org_id,
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(),
)?
};
let current_org_id = required_current_org_id_from_token_str(&(*token).clone())?;
let res_exist = TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
&id_teacher,
&id_student,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
if res_exist {
return Err(anyhow!(HtyErr {
code: HtyErrCode::WebErr,
@@ -3244,7 +3242,7 @@ pub async fn raw_claim_student(
id: uuid(),
status: String::from("Approved"),
created_at: Some(current_local_datetime()),
org_id: current_org_id,
org_id: Some(current_org_id),
};
let res = TeacherStudent::create(
&in_record,
@@ -3340,42 +3338,25 @@ pub async fn raw_disclaim_student(
.clone()
.ok_or_else(|| anyhow!("id_student is required"))?;
let current_org_id = current_org_id_from_token_str(&(*auth).clone());
let res_exist = if let Some(org_id) = &current_org_id {
TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
&id_teacher,
&id_student,
org_id,
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(),
)?
};
let current_org_id = required_current_org_id_from_token_str(&(*auth).clone())?;
let res_exist = TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
&id_teacher,
&id_student,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
if !res_exist {
return Err(anyhow!(HtyErr {
code: HtyErrCode::WebErr,
reason: Some("no existing record for this teacher and student".into()),
}));
}
let res = if let Some(org_id) = &current_org_id {
TeacherStudent::del_by_teacher_id_and_student_id_in_org(
&id_teacher,
&id_student,
org_id,
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())?
};
let res = TeacherStudent::del_by_teacher_id_and_student_id_in_org(
&id_teacher,
&id_student,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
Ok(res)
}
@@ -3579,19 +3560,12 @@ pub async fn raw_find_all_teachers_by_student_id(
student_id: String,
db_pool: Arc<DbState>,
) -> anyhow::Result<Vec<ReqHtyUserWithInfos>> {
let current_org_id = current_org_id_from_token_str(&root.0);
let teacher_ids = if let Some(org_id) = &current_org_id {
TeacherStudent::get_all_teachers_by_student_id_in_org(
&student_id,
org_id,
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(),
)?
};
let current_org_id = required_current_org_id_from_token_str(&root.0)?;
let teacher_ids = TeacherStudent::get_all_teachers_by_student_id_in_org(
&student_id,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
debug!(
"raw_find_all_teachers_by_student_id -> 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 _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 is_exist = if let Some(org_id) = &current_org_id {
TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
&teacher_id,
&student_id,
org_id,
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(),
)?
};
let current_org_id = required_current_org_id_from_token_str(&root.0)?;
let is_exist = TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
&teacher_id,
&student_id,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
if !is_exist {
return Err(anyhow!(HtyErr {
@@ -3682,20 +3648,12 @@ pub async fn raw_delete_teacher_student(
// )?;
// record.status = status;
if let Some(org_id) = &current_org_id {
let _ = TeacherStudent::del_by_teacher_id_and_student_id_in_org(
&teacher_id,
&student_id,
org_id,
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(),
)?;
}
let _ = TeacherStudent::del_by_teacher_id_and_student_id_in_org(
&teacher_id,
&student_id,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
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 _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 is_exist = if let Some(org_id) = &current_org_id {
TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
&teacher_id,
&student_id,
org_id,
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(),
)?
};
let current_org_id = required_current_org_id_from_token_str(&root.0)?;
let is_exist = TeacherStudent::verify_exist_by_teacher_id_and_student_id_in_org(
&teacher_id,
&student_id,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
if !is_exist {
return Err(anyhow!(HtyErr {
code: HtyErrCode::NullErr,
reason: Some("no record for this teacher and student".to_string()),
}));
}
let mut record = if let Some(org_id) = &current_org_id {
TeacherStudent::find_by_teacher_id_and_student_id_in_org(
&teacher_id,
&student_id,
org_id,
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(),
)?
};
let mut record = TeacherStudent::find_by_teacher_id_and_student_id_in_org(
&teacher_id,
&student_id,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
record.status = status;
let _ = TeacherStudent::update(&record, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?;
Ok(())
@@ -3834,21 +3776,13 @@ pub async fn raw_find_all_students_by_teacher_id_and_status(
let status = req_teacher_student
.status
.ok_or_else(|| anyhow!("status is required"))?;
let current_org_id = current_org_id_from_token_str(&root.0);
let student_ids = if let Some(org_id) = &current_org_id {
TeacherStudent::get_all_students_by_teacher_id_and_status_in_org(
&teacher_id,
&status,
org_id,
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(),
)?
};
let current_org_id = required_current_org_id_from_token_str(&root.0)?;
let student_ids = TeacherStudent::get_all_students_by_teacher_id_and_status_in_org(
&teacher_id,
&status,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
debug!(
"raw_find_all_students_by_teacher_id_and_status -> 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> {
match diesel::delete(teacher_student::table).execute(conn) {
Ok(num) => Ok(num),