use crate::casbin::*; use anyhow::anyhow; use appflowy_cloud::biz; use appflowy_cloud::biz::casbin::adapter::PgAdapter; use appflowy_cloud::biz::casbin::{Action, ObjectType}; use casbin::{CoreApi, DefaultModel, Enforcer}; use database_entity::dto::{AFAccessLevel, AFRole}; use shared_entity::dto::workspace_dto::CreateWorkspaceMember; use sqlx::PgPool; #[sqlx::test(migrations = false)] async fn test_create_user(pool: PgPool) -> anyhow::Result<()> { setup_db(&pool).await?; let user = create_user(&pool).await?; // Get workspace details let workspace = database::workspace::select_user_workspace(&pool, &user.uuid) .await? .into_iter() .next() .ok_or(anyhow!("workspace should be created"))?; let model = DefaultModel::from_str(MODEL_CONF).await?; let enforcer = Enforcer::new(model, PgAdapter::new(pool.clone())).await?; assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Workspace(&workspace.workspace_id.to_string()).to_string(), i32::from(AFRole::Owner).to_string(), )) .context("user should be owner of its workspace")?); assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), i32::from(AFAccessLevel::FullAccess).to_string(), )) .context("user should have full access of its collab")?); assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Read.to_string(), )) .context("user should be able to read its collab")?); assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Write.to_string(), )) .context("user should be able to write its collab")?); assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Delete.to_string(), )) .context("user should be able to delete its collab")?); Ok(()) } #[sqlx::test(migrations = false)] async fn test_add_users_to_workspace(pool: PgPool) -> anyhow::Result<()> { setup_db(&pool).await?; let user_main = create_user(&pool).await?; let user_owner = create_user(&pool).await?; let user_member = create_user(&pool).await?; let user_guest = create_user(&pool).await?; // Get workspace details let workspace = database::workspace::select_user_workspace(&pool, &user_main.uuid) .await? .into_iter() .next() .ok_or(anyhow!("workspace should be created"))?; let members = vec![ CreateWorkspaceMember { email: user_owner.email.clone(), role: AFRole::Owner, }, CreateWorkspaceMember { email: user_member.email.clone(), role: AFRole::Member, }, CreateWorkspaceMember { email: user_guest.email.clone(), role: AFRole::Guest, }, ]; let _ = biz::workspace::ops::add_workspace_members( &pool, &user_main.uuid, &workspace.workspace_id, members, ) .await .context("adding users to workspace")?; let model = DefaultModel::from_str(MODEL_CONF).await?; let enforcer = Enforcer::new(model, PgAdapter::new(pool.clone())).await?; { // Owner let user = user_owner; assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), i32::from(AFAccessLevel::FullAccess).to_string(), )) .context("owner should have full access of its collab")?); assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Read.to_string(), )) .context("user should be able to read its collab")?); assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Write.to_string(), )) .context("user should be able to write its collab")?); assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Delete.to_string(), )) .context("user should be able to delete its collab")?); } { // Member let user = user_member; assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), i32::from(AFAccessLevel::ReadAndWrite).to_string(), )) .context("member should have read write access of its collab")?); assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Read.to_string(), )) .context("user should be able to read its collab")?); assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Write.to_string(), )) .context("user should be able to write its collab")?); assert!(!enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Delete.to_string(), )) .context("user should not be able to delete its collab")?); } { // Guest let user = user_guest; assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), i32::from(AFAccessLevel::ReadOnly).to_string(), )) .context("guest should have read only access of its collab")?); assert!(enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Read.to_string(), )) .context("user should not be able to read its collab")?); assert!(!enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Write.to_string(), )) .context("user should not be able to write its collab")?); assert!(!enforcer .enforce(( user.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), Action::Delete.to_string(), )) .context("user should not be able to delete its collab")?); } Ok(()) } #[sqlx::test(migrations = false)] async fn test_reload_policy_after_adding_user_to_workspace(pool: PgPool) -> anyhow::Result<()> { setup_db(&pool).await?; let user_owner = create_user(&pool).await?; let user_member = create_user(&pool).await?; // Get workspace details let workspace = database::workspace::select_user_workspace(&pool, &user_owner.uuid) .await? .into_iter() .next() .ok_or(anyhow!("workspace should be created"))?; // Create enforcer before adding user to workspace let model = DefaultModel::from_str(MODEL_CONF).await?; let mut enforcer = Enforcer::new(model, PgAdapter::new(pool.clone())).await?; let members = vec![CreateWorkspaceMember { email: user_member.email.clone(), role: AFRole::Member, }]; let _ = biz::workspace::ops::add_workspace_members( &pool, &user_owner.uuid, &workspace.workspace_id, members, ) .await .context("adding users to workspace")?; assert!(!enforcer .enforce(( user_member.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), i32::from(AFAccessLevel::ReadAndWrite).to_string(), )) .context("member should not have read write access to collab before reload")?); enforcer.load_policy().await?; assert!(enforcer .enforce(( user_member.uid.to_string(), ObjectType::Collab(&workspace.workspace_id.to_string()).to_string(), i32::from(AFAccessLevel::ReadAndWrite).to_string(), )) .context("member should have read write access to collab")?); Ok(()) }