mirror of https://github.com/buster-so/buster.git
Merge branch 'dallin/bus-920-feature-finish-rest-of-permissions' of https://github.com/buster-so/buster into dallin/bus-920-feature-finish-rest-of-permissions
This commit is contained in:
commit
2f995bf2f0
|
@ -15,7 +15,23 @@ allow_columns_to_appear_in_same_group_by_clause!(
|
||||||
teams::name,
|
teams::name,
|
||||||
permission_groups_to_identities::permission_group_id,
|
permission_groups_to_identities::permission_group_id,
|
||||||
teams_to_users::user_id,
|
teams_to_users::user_id,
|
||||||
teams_to_users::role
|
teams_to_users::role,
|
||||||
|
permission_groups::id,
|
||||||
|
permission_groups::name,
|
||||||
|
permission_groups_to_identities::identity_id,
|
||||||
|
permission_groups_to_identities::identity_type,
|
||||||
|
users::id,
|
||||||
|
users::name,
|
||||||
|
users::email,
|
||||||
|
users_to_organizations::role,
|
||||||
|
datasets::id,
|
||||||
|
datasets::name,
|
||||||
|
datasets::created_at,
|
||||||
|
datasets::updated_at,
|
||||||
|
datasets::enabled,
|
||||||
|
datasets::imported,
|
||||||
|
data_sources::id,
|
||||||
|
data_sources::name,
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Queryable, Insertable, Identifiable, Associations, Debug)]
|
#[derive(Queryable, Insertable, Identifiable, Associations, Debug)]
|
||||||
|
|
|
@ -7,9 +7,12 @@ use diesel_async::RunQueryDsl;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::database::enums::IdentityType;
|
||||||
use crate::database::lib::get_pg_pool;
|
use crate::database::lib::get_pg_pool;
|
||||||
use crate::database::models::{PermissionGroup, User};
|
use crate::database::models::User;
|
||||||
use crate::database::schema::permission_groups;
|
use crate::database::schema::{
|
||||||
|
dataset_permissions, permission_groups, permission_groups_to_identities,
|
||||||
|
};
|
||||||
use crate::routes::rest::ApiResponse;
|
use crate::routes::rest::ApiResponse;
|
||||||
use crate::utils::security::checks::is_user_workspace_admin_or_data_admin;
|
use crate::utils::security::checks::is_user_workspace_admin_or_data_admin;
|
||||||
use crate::utils::user::user_info::get_user_organization_id;
|
use crate::utils::user::user_info::get_user_organization_id;
|
||||||
|
@ -18,9 +21,8 @@ use crate::utils::user::user_info::get_user_organization_id;
|
||||||
pub struct PermissionGroupInfo {
|
pub struct PermissionGroupInfo {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub organization_id: Uuid,
|
pub dataset_count: i64,
|
||||||
pub created_at: DateTime<Utc>,
|
pub assigned: bool,
|
||||||
pub updated_at: DateTime<Utc>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_permission_groups(
|
pub async fn list_permission_groups(
|
||||||
|
@ -45,24 +47,59 @@ async fn list_permission_groups_handler(user: User) -> Result<Vec<PermissionGrou
|
||||||
let organization_id = get_user_organization_id(&user.id).await?;
|
let organization_id = get_user_organization_id(&user.id).await?;
|
||||||
|
|
||||||
if !is_user_workspace_admin_or_data_admin(&user, &organization_id).await? {
|
if !is_user_workspace_admin_or_data_admin(&user, &organization_id).await? {
|
||||||
return Err(anyhow::anyhow!("User is not authorized to list permission groups"));
|
return Err(anyhow::anyhow!(
|
||||||
|
"User is not authorized to list permission groups"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let groups: Vec<PermissionGroup> = permission_groups::table
|
let groups = permission_groups::table
|
||||||
|
.left_join(
|
||||||
|
permission_groups_to_identities::table.on(
|
||||||
|
permission_groups_to_identities::permission_group_id
|
||||||
|
.eq(permission_groups::id)
|
||||||
|
.and(permission_groups_to_identities::deleted_at.is_null())
|
||||||
|
.and(permission_groups_to_identities::identity_id.eq(user.id))
|
||||||
|
.and(permission_groups_to_identities::identity_type.eq(IdentityType::User)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.left_join(
|
||||||
|
dataset_permissions::table.on(dataset_permissions::permission_id
|
||||||
|
.eq(permission_groups::id)
|
||||||
|
.and(dataset_permissions::permission_type.eq("permission_group"))
|
||||||
|
.and(dataset_permissions::deleted_at.is_null())
|
||||||
|
.and(dataset_permissions::organization_id.eq(organization_id))),
|
||||||
|
)
|
||||||
|
.select((
|
||||||
|
permission_groups::id,
|
||||||
|
permission_groups::name,
|
||||||
|
diesel::dsl::sql::<diesel::sql_types::BigInt>(
|
||||||
|
"COALESCE(count(dataset_permissions.id), 0)",
|
||||||
|
),
|
||||||
|
diesel::dsl::sql::<diesel::sql_types::Bool>(
|
||||||
|
"permission_groups_to_identities.identity_id IS NOT NULL",
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.group_by((
|
||||||
|
permission_groups::id,
|
||||||
|
permission_groups::name,
|
||||||
|
dataset_permissions::id,
|
||||||
|
permission_groups_to_identities::identity_id,
|
||||||
|
))
|
||||||
.filter(permission_groups::organization_id.eq(organization_id))
|
.filter(permission_groups::organization_id.eq(organization_id))
|
||||||
.filter(permission_groups::deleted_at.is_null())
|
.filter(permission_groups::deleted_at.is_null())
|
||||||
.order_by(permission_groups::created_at.desc())
|
.order_by(permission_groups::created_at.desc())
|
||||||
.load(&mut *conn)
|
.load::<(Uuid, String, i64, bool)>(&mut *conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(groups
|
Ok(groups
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|group| PermissionGroupInfo {
|
.map(
|
||||||
id: group.id,
|
|(id, name, dataset_count, assigned)| PermissionGroupInfo {
|
||||||
name: group.name,
|
id,
|
||||||
organization_id: group.organization_id,
|
name,
|
||||||
created_at: group.created_at,
|
dataset_count,
|
||||||
updated_at: group.updated_at,
|
assigned,
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use axum::{routing::get, Router};
|
use axum::{routing::get, routing::put, Router};
|
||||||
|
|
||||||
pub mod list_attributes;
|
pub mod list_attributes;
|
||||||
pub mod list_dataset_groups;
|
pub mod list_dataset_groups;
|
||||||
pub mod list_datasets;
|
pub mod list_datasets;
|
||||||
pub mod list_permission_groups;
|
pub mod list_permission_groups;
|
||||||
pub mod list_teams;
|
pub mod list_teams;
|
||||||
|
pub mod put_teams;
|
||||||
|
|
||||||
pub fn router() -> Router {
|
pub fn router() -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
|
@ -19,4 +20,5 @@ pub fn router() -> Router {
|
||||||
get(list_permission_groups::list_permission_groups),
|
get(list_permission_groups::list_permission_groups),
|
||||||
)
|
)
|
||||||
.route("/teams", get(list_teams::list_teams))
|
.route("/teams", get(list_teams::list_teams))
|
||||||
|
.route("/teams", put(put_teams::put_teams))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use axum::extract::Path;
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use axum::{Extension, Json};
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use diesel_async::RunQueryDsl;
|
||||||
|
use serde::Serialize;
|
||||||
|
use tokio::spawn;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::database::lib::get_pg_pool;
|
||||||
|
use crate::database::models::User;
|
||||||
|
use crate::database::schema::{teams_to_users};
|
||||||
|
use crate::routes::rest::ApiResponse;
|
||||||
|
use crate::utils::security::checks::is_user_workspace_admin_or_data_admin;
|
||||||
|
use crate::utils::user::user_info::get_user_organization_id;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct TeamAssignment {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub assigned: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn put_teams(
|
||||||
|
Extension(user): Extension<User>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(assignments): Json<Vec<TeamAssignment>>,
|
||||||
|
) -> Result<ApiResponse<()>, (StatusCode, &'static str)> {
|
||||||
|
match put_teams_handler(user, id, assignments).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Error listing teams: {:?}", e);
|
||||||
|
return Err((StatusCode::INTERNAL_SERVER_ERROR, "Error listing teams"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ApiResponse::NoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn put_teams_handler(
|
||||||
|
user: User,
|
||||||
|
user_id: Uuid,
|
||||||
|
assignments: Vec<TeamAssignment>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let organization_id = get_user_organization_id(&user_id).await?;
|
||||||
|
|
||||||
|
if !is_user_workspace_admin_or_data_admin(&user, &organization_id).await? {
|
||||||
|
return Err(anyhow::anyhow!("User is not authorized to list teams"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let (to_assign, to_unassign): (Vec<_>, Vec<_>) =
|
||||||
|
assignments.into_iter().partition(|a| a.assigned);
|
||||||
|
|
||||||
|
let assign_handle = {
|
||||||
|
let user_id = user_id;
|
||||||
|
spawn(async move {
|
||||||
|
if !to_assign.is_empty() {
|
||||||
|
let mut conn = get_pg_pool().get().await?;
|
||||||
|
for team in to_assign {
|
||||||
|
diesel::insert_into(teams_to_users::table)
|
||||||
|
.values((
|
||||||
|
teams_to_users::team_id.eq(team.id),
|
||||||
|
teams_to_users::user_id.eq(user_id),
|
||||||
|
))
|
||||||
|
.on_conflict((teams_to_users::team_id, teams_to_users::user_id))
|
||||||
|
.do_update()
|
||||||
|
.set(teams_to_users::deleted_at.eq(None::<chrono::DateTime<chrono::Utc>>))
|
||||||
|
.execute(&mut *conn)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok::<_, anyhow::Error>(())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let unassign_handle = {
|
||||||
|
let user_id = user_id;
|
||||||
|
spawn(async move {
|
||||||
|
if !to_unassign.is_empty() {
|
||||||
|
let mut conn = get_pg_pool().get().await?;
|
||||||
|
diesel::update(teams_to_users::table)
|
||||||
|
.filter(
|
||||||
|
teams_to_users::team_id
|
||||||
|
.eq_any(to_unassign.iter().map(|a| a.id))
|
||||||
|
.and(teams_to_users::user_id.eq(user_id)),
|
||||||
|
)
|
||||||
|
.set(teams_to_users::deleted_at.eq(chrono::Utc::now()))
|
||||||
|
.execute(&mut *conn)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
Ok::<_, anyhow::Error>(())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let (assign_result, unassign_result) = tokio::try_join!(assign_handle, unassign_handle)?;
|
||||||
|
assign_result?;
|
||||||
|
unassign_result?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
allow_columns_to_appear_in_same_group_by_clause,
|
|
||||||
dsl::sql,
|
dsl::sql,
|
||||||
sql_types::{Nullable, Timestamptz},
|
sql_types::{Nullable, Timestamptz},
|
||||||
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl,
|
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl,
|
||||||
|
@ -78,20 +77,6 @@ pub struct ListDatasetObject {
|
||||||
pub belongs_to: Option<bool>,
|
pub belongs_to: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
allow_columns_to_appear_in_same_group_by_clause!(
|
|
||||||
datasets::id,
|
|
||||||
datasets::name,
|
|
||||||
datasets::created_at,
|
|
||||||
datasets::updated_at,
|
|
||||||
datasets::enabled,
|
|
||||||
datasets::imported,
|
|
||||||
users::id,
|
|
||||||
users::name,
|
|
||||||
users::email,
|
|
||||||
data_sources::id,
|
|
||||||
data_sources::name,
|
|
||||||
);
|
|
||||||
|
|
||||||
pub async fn list_datasets(user: &User, req: ListDatasetsRequest) -> Result<()> {
|
pub async fn list_datasets(user: &User, req: ListDatasetsRequest) -> Result<()> {
|
||||||
let list_dashboards_res = match list_datasets_handler(
|
let list_dashboards_res = match list_datasets_handler(
|
||||||
&user.id,
|
&user.id,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
allow_columns_to_appear_in_same_group_by_clause,
|
|
||||||
dsl::sql,
|
dsl::sql,
|
||||||
sql_types::{Array, BigInt, Text, Uuid as DieselUuid},
|
sql_types::{Array, BigInt, Text, Uuid as DieselUuid},
|
||||||
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl,
|
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl,
|
||||||
|
@ -65,12 +64,6 @@ pub struct PermissionGroupInfo {
|
||||||
pub belongs_to: Option<bool>,
|
pub belongs_to: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
allow_columns_to_appear_in_same_group_by_clause!(
|
|
||||||
permission_groups::id,
|
|
||||||
permission_groups::name,
|
|
||||||
permission_groups_to_identities::identity_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
pub async fn list_permission_groups(user: &User, req: ListPermissionGroupsRequest) -> Result<()> {
|
pub async fn list_permission_groups(user: &User, req: ListPermissionGroupsRequest) -> Result<()> {
|
||||||
let permission_groups =
|
let permission_groups =
|
||||||
match list_permission_groups_handler(user, req.page, req.page_size, req.filters).await {
|
match list_permission_groups_handler(user, req.page, req.page_size, req.filters).await {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
allow_columns_to_appear_in_same_group_by_clause, dsl::sql, sql_types::BigInt,
|
dsl::sql, sql_types::BigInt,
|
||||||
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl,
|
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
alias, allow_columns_to_appear_in_same_group_by_clause,
|
alias,
|
||||||
dsl::sql,
|
dsl::sql,
|
||||||
sql_types::{Array, BigInt, Text, Uuid as DieselUuid},
|
sql_types::{Array, BigInt, Text, Uuid as DieselUuid},
|
||||||
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl,
|
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl,
|
||||||
|
@ -160,14 +160,6 @@ async fn list_all_users(
|
||||||
|
|
||||||
let team_pgi = alias!(permission_groups_to_identities as team_pgi);
|
let team_pgi = alias!(permission_groups_to_identities as team_pgi);
|
||||||
|
|
||||||
allow_columns_to_appear_in_same_group_by_clause!(
|
|
||||||
users::id,
|
|
||||||
users::name,
|
|
||||||
users::email,
|
|
||||||
users_to_organizations::role,
|
|
||||||
permission_groups_to_identities::permission_group_id
|
|
||||||
);
|
|
||||||
|
|
||||||
let user_permissions: Vec<(Uuid, Option<String>, String, i64, i64, UserOrganizationRole)> = match users::table
|
let user_permissions: Vec<(Uuid, Option<String>, String, i64, i64, UserOrganizationRole)> = match users::table
|
||||||
.left_join(teams_to_users::table.on(users::id.eq(teams_to_users::user_id).and(teams_to_users::deleted_at.is_null())))
|
.left_join(teams_to_users::table.on(users::id.eq(teams_to_users::user_id).and(teams_to_users::deleted_at.is_null())))
|
||||||
.inner_join(users_to_organizations::table.on(users::id.eq(users_to_organizations::user_id).and(users_to_organizations::deleted_at.is_null())))
|
.inner_join(users_to_organizations::table.on(users::id.eq(users_to_organizations::user_id).and(users_to_organizations::deleted_at.is_null())))
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
alias, allow_columns_to_appear_in_same_group_by_clause,
|
alias,
|
||||||
deserialize::QueryableByName,
|
deserialize::QueryableByName,
|
||||||
dsl::{count, not, sql},
|
dsl::{count, not, sql},
|
||||||
insert_into,
|
insert_into,
|
||||||
|
@ -523,12 +523,6 @@ async fn get_user_queries_last_30_days(user_id: Arc<Uuid>) -> Result<i64> {
|
||||||
async fn get_user_permission_groups(user_id: Arc<Uuid>) -> Result<Vec<PermissionGroupItem>> {
|
async fn get_user_permission_groups(user_id: Arc<Uuid>) -> Result<Vec<PermissionGroupItem>> {
|
||||||
let mut conn = get_pg_pool().get().await?;
|
let mut conn = get_pg_pool().get().await?;
|
||||||
|
|
||||||
allow_columns_to_appear_in_same_group_by_clause!(
|
|
||||||
permission_groups::id,
|
|
||||||
permission_groups::name,
|
|
||||||
permission_groups_to_identities::identity_type,
|
|
||||||
);
|
|
||||||
|
|
||||||
let permission_groups = match permission_groups::table
|
let permission_groups = match permission_groups::table
|
||||||
.inner_join(
|
.inner_join(
|
||||||
permission_groups_to_identities::table
|
permission_groups_to_identities::table
|
||||||
|
|
Loading…
Reference in New Issue