mirror of https://github.com/buster-so/buster.git
376 lines
13 KiB
Rust
376 lines
13 KiB
Rust
use anyhow::{anyhow, Result};
|
|
use diesel::{
|
|
allow_columns_to_appear_in_same_group_by_clause, dsl::sql, sql_types::BigInt,
|
|
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl,
|
|
};
|
|
use diesel_async::RunQueryDsl;
|
|
use serde::{Deserialize, Serialize};
|
|
use uuid::Uuid;
|
|
|
|
use crate::{
|
|
database::{
|
|
enums::{IdentityType, TeamToUserRole},
|
|
lib::get_pg_pool,
|
|
models::User,
|
|
schema::{permission_groups_to_identities, teams, teams_to_users},
|
|
},
|
|
routes::ws::{
|
|
permissions::permissions_router::{PermissionEvent, PermissionRoute},
|
|
ws::{WsErrorCode, WsEvent, WsResponseMessage, WsSendMethod},
|
|
ws_router::WsRoutes,
|
|
ws_utils::{send_error_message, send_ws_message},
|
|
},
|
|
utils::{clients::sentry_utils::send_sentry_error, user::user_info::get_user_organization_id},
|
|
};
|
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
pub struct ListTeamsFilters {
|
|
pub permission_group_id: Option<Uuid>,
|
|
pub user_id: Option<Uuid>,
|
|
pub belongs_to: Option<bool>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
pub struct ListTeamPermissionsRequest {
|
|
pub page: Option<i64>,
|
|
pub page_size: Option<i64>,
|
|
#[serde(flatten)]
|
|
pub filters: Option<ListTeamsFilters>,
|
|
}
|
|
|
|
#[derive(Serialize, Debug, Clone)]
|
|
pub struct TeamPermissionInfo {
|
|
pub id: Uuid,
|
|
pub name: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub permission_group_count: Option<i64>,
|
|
pub member_count: i64,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub belongs_to: Option<bool>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub team_role: Option<TeamToUserRole>,
|
|
}
|
|
|
|
pub async fn list_teams(user: &User, req: ListTeamPermissionsRequest) -> Result<()> {
|
|
let page = req.page.unwrap_or(0);
|
|
let page_size = req.page_size.unwrap_or(25);
|
|
|
|
let team_permissions =
|
|
match list_team_permissions_handler(user, page, page_size, req.filters).await {
|
|
Ok(team_permissions) => team_permissions,
|
|
Err(e) => {
|
|
tracing::error!("Error listing team permissions: {}", e);
|
|
send_sentry_error(&e.to_string(), Some(&user.id));
|
|
send_error_message(
|
|
&user.id.to_string(),
|
|
WsRoutes::Permissions(PermissionRoute::ListTeamPermissions),
|
|
WsEvent::Permissions(PermissionEvent::ListTeamPermissions),
|
|
WsErrorCode::InternalServerError,
|
|
"Failed to list team permissions.".to_string(),
|
|
user,
|
|
)
|
|
.await?;
|
|
return Err(e);
|
|
}
|
|
};
|
|
|
|
let list_permission_groups_message = WsResponseMessage::new(
|
|
WsRoutes::Permissions(PermissionRoute::ListTeamPermissions),
|
|
WsEvent::Permissions(PermissionEvent::ListTeamPermissions),
|
|
team_permissions,
|
|
None,
|
|
user,
|
|
WsSendMethod::All,
|
|
);
|
|
|
|
match send_ws_message(&user.id.to_string(), &list_permission_groups_message).await {
|
|
Ok(_) => (),
|
|
Err(e) => {
|
|
tracing::error!("Error sending ws message: {}", e);
|
|
let err = anyhow!("Error sending ws message: {}", e);
|
|
send_sentry_error(&e.to_string(), Some(&user.id));
|
|
return Err(err);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn list_team_permissions_handler(
|
|
user: &User,
|
|
page: i64,
|
|
page_size: i64,
|
|
filters: Option<ListTeamsFilters>,
|
|
) -> Result<Vec<TeamPermissionInfo>> {
|
|
let organization_id = match get_user_organization_id(&user.id).await {
|
|
Ok(organization_id) => organization_id,
|
|
Err(e) => return Err(e),
|
|
};
|
|
|
|
let team_permissions = if let Some(filters) = filters {
|
|
if let Some(permission_group_id) = filters.permission_group_id {
|
|
query_permission_groups_teams(
|
|
organization_id,
|
|
permission_group_id,
|
|
page,
|
|
page_size,
|
|
filters.belongs_to,
|
|
)
|
|
.await?
|
|
} else if let Some(user_id) = filters.user_id {
|
|
query_user_teams(
|
|
&organization_id,
|
|
&user_id,
|
|
page,
|
|
page_size,
|
|
filters.belongs_to,
|
|
)
|
|
.await?
|
|
} else {
|
|
list_all_teams(page, page_size, organization_id).await?
|
|
}
|
|
} else {
|
|
list_all_teams(page, page_size, organization_id).await?
|
|
};
|
|
|
|
Ok(team_permissions)
|
|
}
|
|
|
|
async fn list_all_teams(
|
|
page: i64,
|
|
page_size: i64,
|
|
organization_id: Uuid,
|
|
) -> Result<Vec<TeamPermissionInfo>> {
|
|
let mut conn = get_pg_pool().get().await?;
|
|
|
|
let team_permissions = match teams::table
|
|
.left_join(teams_to_users::table.on(teams::id.eq(teams_to_users::team_id)))
|
|
.left_join(
|
|
permission_groups_to_identities::table
|
|
.on(teams::id.eq(permission_groups_to_identities::identity_id)),
|
|
)
|
|
.select((
|
|
teams::id,
|
|
teams::name,
|
|
sql::<BigInt>("count(distinct teams_to_users.user_id)"),
|
|
sql::<BigInt>("count(distinct permission_groups_to_identities.permission_group_id)"),
|
|
))
|
|
.filter(teams::organization_id.eq(&organization_id))
|
|
.filter(teams::deleted_at.is_null())
|
|
.filter(teams_to_users::deleted_at.is_null())
|
|
.filter(permission_groups_to_identities::deleted_at.is_null())
|
|
.group_by((teams::id, teams::name))
|
|
.order(teams::name.asc())
|
|
.limit(page_size)
|
|
.offset(page * page_size)
|
|
.load::<(Uuid, String, i64, i64)>(&mut conn)
|
|
.await
|
|
{
|
|
Ok(team_permissions) => team_permissions,
|
|
Err(e) => return Err(anyhow!("Error loading team permissions: {}", e)),
|
|
};
|
|
|
|
let team_permissions = team_permissions
|
|
.into_iter()
|
|
.map(
|
|
|(id, name, member_count, permission_group_count)| TeamPermissionInfo {
|
|
id,
|
|
name,
|
|
permission_group_count: Some(permission_group_count),
|
|
member_count,
|
|
belongs_to: None,
|
|
team_role: None,
|
|
},
|
|
)
|
|
.collect();
|
|
|
|
Ok(team_permissions)
|
|
}
|
|
|
|
async fn query_permission_groups_teams(
|
|
organization_id: Uuid,
|
|
permission_group_id: Uuid,
|
|
page: i64,
|
|
page_size: i64,
|
|
only_owned: Option<bool>,
|
|
) -> Result<Vec<TeamPermissionInfo>> {
|
|
let mut conn = get_pg_pool().get().await?;
|
|
|
|
let permission_group_team_results = if let Some(_) = only_owned {
|
|
teams::table
|
|
.inner_join(
|
|
permission_groups_to_identities::table.on(teams::id
|
|
.eq(permission_groups_to_identities::identity_id)
|
|
.and(permission_groups_to_identities::identity_type.eq(IdentityType::Team))
|
|
.and(permission_groups_to_identities::deleted_at.is_null())),
|
|
)
|
|
.inner_join(
|
|
teams_to_users::table.on(teams::id
|
|
.eq(teams_to_users::team_id)
|
|
.and(teams_to_users::deleted_at.is_null())),
|
|
)
|
|
.select((
|
|
teams::id,
|
|
teams::name,
|
|
permission_groups_to_identities::permission_group_id.nullable(),
|
|
sql::<BigInt>("count(distinct teams_to_users.user_id)"),
|
|
))
|
|
.group_by((
|
|
teams::id,
|
|
teams::name,
|
|
permission_groups_to_identities::permission_group_id,
|
|
))
|
|
.filter(permission_groups_to_identities::permission_group_id.eq(&permission_group_id))
|
|
.filter(teams::organization_id.eq(organization_id))
|
|
.filter(teams::deleted_at.is_null())
|
|
.limit(page_size)
|
|
.offset(page * page_size)
|
|
.load::<(Uuid, String, Option<Uuid>, i64)>(&mut conn)
|
|
.await
|
|
} else {
|
|
teams::table
|
|
.left_join(
|
|
permission_groups_to_identities::table.on(teams::id
|
|
.eq(permission_groups_to_identities::identity_id)
|
|
.and(
|
|
permission_groups_to_identities::identity_type
|
|
.eq(IdentityType::Team)
|
|
.and(permission_groups_to_identities::deleted_at.is_null())
|
|
.and(
|
|
permission_groups_to_identities::permission_group_id
|
|
.eq(permission_group_id),
|
|
),
|
|
)),
|
|
)
|
|
.inner_join(
|
|
teams_to_users::table.on(teams::id
|
|
.eq(teams_to_users::team_id)
|
|
.and(teams_to_users::deleted_at.is_null())),
|
|
)
|
|
.select((
|
|
teams::id,
|
|
teams::name,
|
|
permission_groups_to_identities::permission_group_id.nullable(),
|
|
sql::<BigInt>("count(distinct teams_to_users.user_id)"),
|
|
))
|
|
.group_by((
|
|
teams::id,
|
|
teams::name,
|
|
permission_groups_to_identities::permission_group_id,
|
|
))
|
|
.filter(teams::deleted_at.is_null())
|
|
.filter(teams::organization_id.eq(organization_id))
|
|
.limit(page_size)
|
|
.offset(page * page_size)
|
|
.load::<(Uuid, String, Option<Uuid>, i64)>(&mut conn)
|
|
.await
|
|
};
|
|
|
|
let permission_group_team_results: Vec<(Uuid, String, Option<Uuid>, i64)> =
|
|
match permission_group_team_results {
|
|
Ok(teams) => teams,
|
|
Err(e) => return Err(anyhow!("Error getting teams: {}", e)),
|
|
};
|
|
|
|
let team_objects: Vec<TeamPermissionInfo> = permission_group_team_results
|
|
.into_iter()
|
|
.map(
|
|
|(id, name, permission_group_id, member_count)| TeamPermissionInfo {
|
|
id,
|
|
name,
|
|
permission_group_count: None,
|
|
member_count,
|
|
belongs_to: Some(permission_group_id.is_some()),
|
|
team_role: None,
|
|
},
|
|
)
|
|
.collect();
|
|
|
|
Ok(team_objects)
|
|
}
|
|
|
|
async fn query_user_teams(
|
|
organization_id: &Uuid,
|
|
user_id: &Uuid, // this is the user being queried, not the user who made the req.
|
|
page: i64,
|
|
page_size: i64,
|
|
only_owned: Option<bool>,
|
|
) -> Result<Vec<TeamPermissionInfo>> {
|
|
let mut conn = get_pg_pool().get().await?;
|
|
|
|
let team_results = if let Some(true) = only_owned {
|
|
teams::table
|
|
.inner_join(teams_to_users::table)
|
|
.select((
|
|
teams::id,
|
|
teams::name,
|
|
teams_to_users::user_id.nullable(),
|
|
teams_to_users::role.nullable(),
|
|
sql::<BigInt>("count(distinct teams_to_users.user_id)"),
|
|
))
|
|
.group_by((
|
|
teams::id,
|
|
teams::name,
|
|
teams_to_users::user_id,
|
|
teams_to_users::role,
|
|
))
|
|
.filter(teams_to_users::user_id.eq(user_id))
|
|
.filter(teams::organization_id.eq(organization_id))
|
|
.filter(teams::deleted_at.is_null())
|
|
.filter(teams_to_users::deleted_at.is_null())
|
|
.limit(page_size)
|
|
.offset(page * page_size)
|
|
.load::<(Uuid, String, Option<Uuid>, Option<TeamToUserRole>, i64)>(&mut conn)
|
|
.await
|
|
} else {
|
|
teams::table
|
|
.left_join(
|
|
teams_to_users::table.on(teams::id
|
|
.eq(teams_to_users::team_id)
|
|
.and(teams_to_users::user_id.eq(user_id))
|
|
.and(teams_to_users::deleted_at.is_null())),
|
|
)
|
|
.select((
|
|
teams::id,
|
|
teams::name,
|
|
teams_to_users::user_id.nullable(),
|
|
teams_to_users::role.nullable(),
|
|
sql::<BigInt>("count(distinct teams_to_users.user_id)"),
|
|
))
|
|
.group_by((
|
|
teams::id,
|
|
teams::name,
|
|
teams_to_users::user_id,
|
|
teams_to_users::role,
|
|
))
|
|
.filter(teams::organization_id.eq(organization_id))
|
|
.filter(teams::deleted_at.is_null())
|
|
.limit(page_size)
|
|
.offset(page * page_size)
|
|
.load::<(Uuid, String, Option<Uuid>, Option<TeamToUserRole>, i64)>(&mut conn)
|
|
.await
|
|
};
|
|
|
|
let team_results: Vec<(Uuid, String, Option<Uuid>, Option<TeamToUserRole>, i64)> =
|
|
match team_results {
|
|
Ok(teams) => teams,
|
|
Err(e) => return Err(anyhow!("Error getting teams: {}", e)),
|
|
};
|
|
|
|
let team_objects = team_results
|
|
.into_iter()
|
|
.map(
|
|
|(id, name, user_id, team_role, member_count)| TeamPermissionInfo {
|
|
id,
|
|
name,
|
|
belongs_to: Some(user_id.is_some()),
|
|
permission_group_count: None,
|
|
member_count,
|
|
team_role,
|
|
},
|
|
)
|
|
.collect();
|
|
|
|
Ok(team_objects)
|
|
}
|