Skip to content
Clone
ef0b83a
115 lines 3.3 KB Rust
use std::collections::{HashMap, HashSet};
use models::dir::{dir_overwrite_group, dir_overwrite_users, dirs};
use models::space::{space_member_groups, spaces};
use sea_orm::{ColumnTrait, ConnectionTrait, EntityTrait, QueryFilter};
use uuid::Uuid;
use crate::errors::SpaceError;
pub const PERM_DIR_VISIBLE: i64 = 1 << 0;
fn node_visible(
caller: i64,
my_groups: &HashSet<Uuid>,
group_ows: &[dir_overwrite_group::Model],
user_ows: &[dir_overwrite_users::Model],
) -> bool {
if group_ows.is_empty() && user_ows.is_empty() {
return true;
}
if let Some(uo) = user_ows.iter().find(|u| u.user_id == caller) {
if (uo.deny_mask & PERM_DIR_VISIBLE) != 0 {
return false;
}
if (uo.allow_mask & PERM_DIR_VISIBLE) != 0 {
return true;
}
}
let matching: Vec<&dir_overwrite_group::Model> = group_ows
.iter()
.filter(|g| my_groups.contains(&g.group_id))
.collect();
if matching
.iter()
.any(|g| (g.deny_mask & PERM_DIR_VISIBLE) != 0)
{
return false;
}
matching
.iter()
.any(|g| (g.allow_mask & PERM_DIR_VISIBLE) != 0)
}
pub async fn is_dir_visible(
db: &impl ConnectionTrait,
space: &spaces::Model,
dir_id: Uuid,
caller: i64,
) -> Result<bool, SpaceError> {
if space.owner_user_id == caller {
return Ok(true);
}
let target = dirs::Entity::find_by_id(dir_id).one(db).await?;
let Some(target) = target else {
return Ok(false);
};
let my_groups: HashSet<Uuid> = space_member_groups::Entity::find()
.filter(space_member_groups::Column::SpaceId.eq(space.id))
.filter(space_member_groups::Column::UserId.eq(caller))
.all(db)
.await?
.into_iter()
.map(|mg| mg.group_id)
.collect();
let nodes = dirs::Entity::find()
.filter(dirs::Column::SpaceId.eq(space.id))
.all(db)
.await?;
let group_ows = dir_overwrite_group::Entity::find()
.filter(dir_overwrite_group::Column::DirId.is_in(nodes.iter().map(|n| n.id)))
.all(db)
.await?;
let user_ows = dir_overwrite_users::Entity::find()
.filter(dir_overwrite_users::Column::DirId.is_in(nodes.iter().map(|n| n.id)))
.all(db)
.await?;
let mut gmap: HashMap<Uuid, Vec<dir_overwrite_group::Model>> = HashMap::new();
for g in group_ows {
gmap.entry(g.dir_id).or_default().push(g);
}
let mut umap: HashMap<Uuid, Vec<dir_overwrite_users::Model>> = HashMap::new();
for u in user_ows {
umap.entry(u.dir_id).or_default().push(u);
}
let mut parent_map: HashMap<Uuid, Option<Uuid>> = HashMap::new();
for n in &nodes {
parent_map.insert(n.id, n.parent_dir_id);
}
let direct = |id: Uuid| -> bool {
node_visible(
caller,
&my_groups,
gmap.get(&id).map(Vec::as_slice).unwrap_or(&[]),
umap.get(&id).map(Vec::as_slice).unwrap_or(&[]),
)
};
let mut current: Option<Uuid> = Some(target.id);
let mut visited = HashSet::new();
while let Some(id) = current {
if !visited.insert(id) {
return Ok(false);
}
if !direct(id) {
return Ok(false);
}
current = parent_map.get(&id).copied().flatten();
}
Ok(true)
}