Add loop protection
This commit is contained in:
parent
eaf09135f4
commit
5506149efc
@ -262,6 +262,20 @@ pub async fn update(m: FamilyAndMemberInPath, req: web::Json<MemberRequest>) ->
|
|||||||
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for potential loops
|
||||||
|
let members = members_service::get_all_of_family(member.family_id()).await?;
|
||||||
|
let mut members_ref = Vec::with_capacity(members.len());
|
||||||
|
members_ref.push(&member);
|
||||||
|
for m in &members {
|
||||||
|
if m.id() != member.id() {
|
||||||
|
members_ref.push(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if members_service::loop_detection::detect_loop(&members_ref) {
|
||||||
|
log::warn!("Membership update rejected due to detected loop!");
|
||||||
|
return Ok(HttpResponse::NotAcceptable().json("Loop detected!"));
|
||||||
|
}
|
||||||
|
|
||||||
members_service::update(&mut member).await?;
|
members_service::update(&mut member).await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().finish())
|
Ok(HttpResponse::Accepted().finish())
|
||||||
|
@ -118,7 +118,7 @@ pub struct FamilyMembership {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Member ID holder
|
/// Member ID holder
|
||||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
|
||||||
pub struct MemberID(pub i32);
|
pub struct MemberID(pub i32);
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
@ -106,3 +106,86 @@ pub async fn delete_all_family(family_id: FamilyID) -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod loop_detection {
|
||||||
|
use crate::models::{Member, MemberID};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct LoopStack<'a> {
|
||||||
|
curr: MemberID,
|
||||||
|
prev: Option<&'a LoopStack<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LoopStack<'a> {
|
||||||
|
pub fn contains(&self, id: MemberID) -> bool {
|
||||||
|
if let Some(ls) = &self.prev {
|
||||||
|
if ls.contains(id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.curr == id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect_loop_control(
|
||||||
|
members: &HashMap<MemberID, &&Member>,
|
||||||
|
curr_stack: &LoopStack,
|
||||||
|
next: Option<MemberID>,
|
||||||
|
) -> bool {
|
||||||
|
match next {
|
||||||
|
None => false,
|
||||||
|
Some(id) => {
|
||||||
|
if curr_stack.contains(id) {
|
||||||
|
log::debug!("Loop detected! {:?}", curr_stack);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_loop_recurse(
|
||||||
|
members,
|
||||||
|
&LoopStack {
|
||||||
|
curr: id,
|
||||||
|
prev: Some(curr_stack),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recurse loop detection
|
||||||
|
fn detect_loop_recurse(members: &HashMap<MemberID, &&Member>, curr_stack: &LoopStack) -> bool {
|
||||||
|
let member = match members.get(&curr_stack.curr) {
|
||||||
|
Some(m) => m,
|
||||||
|
None => {
|
||||||
|
log::warn!("Member {:?} not found in the tree for loop detection, this should never happen!", curr_stack.curr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
detect_loop_control(members, curr_stack, member.mother())
|
||||||
|
|| detect_loop_control(members, curr_stack, member.father())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detect loops in members hierarchy
|
||||||
|
pub fn detect_loop(members: &[&Member]) -> bool {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
for m in members {
|
||||||
|
map.insert(m.id(), m);
|
||||||
|
}
|
||||||
|
|
||||||
|
for m in members {
|
||||||
|
if detect_loop_recurse(
|
||||||
|
&map,
|
||||||
|
&LoopStack {
|
||||||
|
curr: m.id(),
|
||||||
|
prev: None,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user