Skip to content
Clone
eba4fd1
147 lines 3.9 KB Rust
use std::collections::HashSet;
use models::space::space_members::MemberStatus;
use models::space::{
space_bans, space_group_permissions, space_member_groups, space_members, spaces,
};
use sea_orm::{ColumnTrait, ConnectionTrait, EntityTrait, QueryFilter};
use time::OffsetDateTime;
use uuid::Uuid;
use crate::errors::SpaceError;
use crate::services::service::SpaceService;
pub const VIEW_SPACE: &str = "view_space";
pub const READ: &str = "read";
pub const MESSAGE: &str = "message";
pub const MANAGE_MEMBERS: &str = "manage_members";
pub const MANAGE_ROLES: &str = "manage_roles";
pub const MANAGE_INVITES: &str = "manage_invites";
pub const MANAGE_BANS: &str = "manage_bans";
pub const MANAGE_CHANNELS: &str = "manage_channels";
pub const MANAGE_OVERWRITES: &str = "manage_overwrites";
pub const ALL_PERMISSIONS: [&str; 9] = [
VIEW_SPACE,
READ,
MESSAGE,
MANAGE_MEMBERS,
MANAGE_ROLES,
MANAGE_INVITES,
MANAGE_BANS,
MANAGE_CHANNELS,
MANAGE_OVERWRITES,
];
#[derive(Debug, Clone, Default)]
pub struct Permissions {
granted: HashSet<String>,
}
impl Permissions {
pub fn owner() -> Self {
Self {
granted: ALL_PERMISSIONS.into_iter().map(String::from).collect(),
}
}
pub fn has(&self, action: &str) -> bool {
self.granted.contains(action)
}
pub fn into_vec(self) -> Vec<String> {
let mut v: Vec<String> = self.granted.into_iter().collect();
v.sort();
v.dedup();
v
}
}
pub async fn assert_not_banned(
db: &impl ConnectionTrait,
space_id: Uuid,
user_id: Uuid,
) -> Result<(), SpaceError> {
if export::authz::is_system_uid(user_id) {
return Ok(());
}
let now = OffsetDateTime::now_utc();
if let Some(ban) = space_bans::Entity::find_by_id((space_id, user_id))
.one(db)
.await?
&& (ban.expires_at.is_none() || ban.expires_at > Some(now))
{
return Err(SpaceError::Forbidden);
}
Ok(())
}
pub async fn effective_permissions(
db: &impl ConnectionTrait,
space: &spaces::Model,
user_id: Uuid,
) -> Result<Permissions, SpaceError> {
if space.owner_id == user_id || export::authz::is_system_uid(user_id) {
return Ok(Permissions::owner());
}
let now = OffsetDateTime::now_utc();
if let Some(ban) = space_bans::Entity::find_by_id((space.id, user_id))
.one(db)
.await?
&& (ban.expires_at.is_none() || ban.expires_at > Some(now))
{
return Err(SpaceError::Forbidden);
}
let Some(member) = space_members::Entity::find_by_id((space.id, user_id))
.one(db)
.await?
else {
return Err(SpaceError::Forbidden);
};
if member.status != MemberStatus::Active {
return Err(SpaceError::Forbidden);
}
let member_groups = space_member_groups::Entity::find()
.filter(space_member_groups::Column::SpaceId.eq(space.id))
.filter(space_member_groups::Column::UserId.eq(user_id))
.all(db)
.await?;
let mut granted: HashSet<String> = [VIEW_SPACE, READ, MESSAGE]
.into_iter()
.map(String::from)
.collect();
if member_groups.is_empty() {
return Ok(Permissions { granted });
}
let group_ids: Vec<Uuid> = member_groups.into_iter().map(|mg| mg.group_id).collect();
let group_perms = space_group_permissions::Entity::find()
.filter(space_group_permissions::Column::GroupId.is_in(group_ids))
.all(db)
.await?;
for gp in group_perms {
granted.insert(gp.permission);
}
Ok(Permissions { granted })
}
pub async fn require_perm(
service: &SpaceService,
space: &spaces::Model,
caller: Uuid,
action: &str,
) -> Result<(), SpaceError> {
let perms = effective_permissions(service.db().read(), space, caller).await?;
if !perms.has(action) {
return Err(SpaceError::Forbidden);
}
Ok(())
}