fix(spaces): use highest-wins PL logic across multiple parent spaces
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Pre-commit & Formatting (pull_request) Failing after 5s
Checks / Prek / Clippy and Cargo Tests (pull_request) Failing after 5s
Update flake hashes / update-flake-hashes (pull_request) Failing after 6s
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Pre-commit & Formatting (pull_request) Failing after 5s
Checks / Prek / Clippy and Cargo Tests (pull_request) Failing after 5s
Update flake hashes / update-flake-hashes (pull_request) Failing after 6s
When a room is a child of multiple spaces, sync_power_levels now computes the maximum power level across ALL parent spaces for each user, not just the triggering space. validate_pl_change similarly computes the effective PL as the max across all parents before rejecting conflicting proposals.
This commit is contained in:
parent
4df2fe2923
commit
982c01e562
1 changed files with 64 additions and 57 deletions
|
|
@ -556,23 +556,33 @@ pub async fn sync_power_levels(&self, space_id: &RoomId, room_id: &RoomId) -> Re
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let all_parents = self.get_parent_spaces(room_id).await;
|
||||||
|
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
for user_id in &members {
|
for user_id in &members {
|
||||||
if user_id == server_user {
|
if user_id == server_user {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(space_pl) = self.get_user_power_level(space_id, user_id).await {
|
|
||||||
let space_pl_int = Int::new_saturating(space_pl);
|
let mut max_pl: Option<i64> = None;
|
||||||
|
for parent in &all_parents {
|
||||||
|
if let Some(pl) = self.get_user_power_level(parent, user_id).await {
|
||||||
|
max_pl = Some(max_pl.map_or(pl, |current| current.max(pl)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(effective_pl) = max_pl {
|
||||||
|
let effective_pl_int = Int::new_saturating(effective_pl);
|
||||||
let current_pl = power_levels_content
|
let current_pl = power_levels_content
|
||||||
.users
|
.users
|
||||||
.get(user_id)
|
.get(user_id)
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or(power_levels_content.users_default);
|
.unwrap_or(power_levels_content.users_default);
|
||||||
|
|
||||||
if current_pl != space_pl_int {
|
if current_pl != effective_pl_int {
|
||||||
power_levels_content
|
power_levels_content
|
||||||
.users
|
.users
|
||||||
.insert(user_id.clone(), space_pl_int);
|
.insert(user_id.clone(), effective_pl_int);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -754,55 +764,54 @@ pub async fn validate_pl_change(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
type SpaceEnforcementData =
|
let mut effective_pls: HashMap<OwnedUserId, i64> = HashMap::new();
|
||||||
(Vec<(OwnedUserId, HashSet<String>)>, BTreeMap<String, RoleDefinition>);
|
{
|
||||||
let space_data: Vec<SpaceEnforcementData> = {
|
|
||||||
let roles_guard = self.roles.read().await;
|
let roles_guard = self.roles.read().await;
|
||||||
let user_roles_guard = self.user_roles.read().await;
|
let user_roles_guard = self.user_roles.read().await;
|
||||||
parent_spaces
|
for ps in &parent_spaces {
|
||||||
.iter()
|
let Some(space_users) = user_roles_guard.get(ps) else {
|
||||||
.filter_map(|ps| {
|
|
||||||
let space_users = user_roles_guard.get(ps)?;
|
|
||||||
let role_defs = roles_guard.get(ps)?;
|
|
||||||
Some((
|
|
||||||
space_users
|
|
||||||
.iter()
|
|
||||||
.map(|(u, r)| (u.clone(), r.clone()))
|
|
||||||
.collect(),
|
|
||||||
role_defs.clone(),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
};
|
|
||||||
|
|
||||||
for (space_users, role_defs) in &space_data {
|
|
||||||
for (user_id, assigned_roles) in space_users {
|
|
||||||
if !self.services.state_cache.is_joined(user_id, room_id).await {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
};
|
||||||
let space_pl = assigned_roles
|
let Some(role_defs) = roles_guard.get(ps) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for (user_id, assigned_roles) in space_users {
|
||||||
|
let pl = assigned_roles
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|r| role_defs.get(r)?.power_level)
|
.filter_map(|r| role_defs.get(r)?.power_level)
|
||||||
.max();
|
.max();
|
||||||
if let Some(space_pl) = space_pl {
|
if let Some(pl) = pl {
|
||||||
|
effective_pls
|
||||||
|
.entry(user_id.clone())
|
||||||
|
.and_modify(|current| *current = (*current).max(pl))
|
||||||
|
.or_insert(pl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (user_id, effective_pl) in &effective_pls {
|
||||||
|
if !self.services.state_cache.is_joined(user_id, room_id).await {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
match proposed.users.get(user_id) {
|
match proposed.users.get(user_id) {
|
||||||
| None if i64::from(proposed.users_default) != space_pl => {
|
| None if i64::from(proposed.users_default) != *effective_pl => {
|
||||||
debug_warn!(
|
debug_warn!(
|
||||||
user_id = %user_id,
|
user_id = %user_id,
|
||||||
room_id = %room_id,
|
room_id = %room_id,
|
||||||
space_pl,
|
effective_pl,
|
||||||
"Rejecting PL change: space-managed user omitted"
|
"Rejecting PL change: space-managed user omitted"
|
||||||
);
|
);
|
||||||
return Err!(Request(Forbidden(
|
return Err!(Request(Forbidden(
|
||||||
"Cannot omit a user whose power level is managed by Space roles"
|
"Cannot omit a user whose power level is managed by Space roles"
|
||||||
)));
|
)));
|
||||||
},
|
},
|
||||||
| Some(pl) if i64::from(*pl) != space_pl => {
|
| Some(pl) if i64::from(*pl) != *effective_pl => {
|
||||||
debug_warn!(
|
debug_warn!(
|
||||||
user_id = %user_id,
|
user_id = %user_id,
|
||||||
room_id = %room_id,
|
room_id = %room_id,
|
||||||
proposed_pl = i64::from(*pl),
|
proposed_pl = i64::from(*pl),
|
||||||
space_pl,
|
effective_pl,
|
||||||
"Rejecting PL change conflicting with space role"
|
"Rejecting PL change conflicting with space role"
|
||||||
);
|
);
|
||||||
return Err!(Request(Forbidden(
|
return Err!(Request(Forbidden(
|
||||||
|
|
@ -812,8 +821,6 @@ pub async fn validate_pl_change(
|
||||||
| _ => {},
|
| _ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue