mirror of https://github.com/buster-so/buster.git
api_permission_field_fix
This commit is contained in:
parent
1684dad78c
commit
eedb717393
|
@ -80,7 +80,7 @@ pub async fn get_collection_handler(
|
|||
|
||||
// Check if user has permission to view the collection
|
||||
// Users need at least CanView permission or any higher permission
|
||||
let has_permission = check_permission_access(
|
||||
if !check_permission_access(
|
||||
collection_with_permission.permission,
|
||||
&[
|
||||
AssetPermissionRole::CanView,
|
||||
|
@ -90,12 +90,15 @@ pub async fn get_collection_handler(
|
|||
],
|
||||
collection_with_permission.collection.organization_id,
|
||||
&user.organizations,
|
||||
);
|
||||
|
||||
if !has_permission {
|
||||
) {
|
||||
return Err(anyhow!("You don't have permission to view this collection"));
|
||||
}
|
||||
|
||||
// Extract permission for consistent use in response
|
||||
// If the asset is public and the user has no direct permission, default to CanView
|
||||
let permission = collection_with_permission.permission
|
||||
.unwrap_or(AssetPermissionRole::CanView);
|
||||
|
||||
let mut conn = match get_pg_pool().get().await {
|
||||
Ok(conn) => conn,
|
||||
Err(e) => return Err(anyhow!("Failed to get database connection: {}", e)),
|
||||
|
@ -232,7 +235,7 @@ pub async fn get_collection_handler(
|
|||
let collection_state = CollectionState {
|
||||
collection: collection_with_permission.collection,
|
||||
assets: Some(formatted_assets),
|
||||
permission: collection_with_permission.permission.unwrap_or(AssetPermissionRole::Owner),
|
||||
permission,
|
||||
organization_permissions: false, // TODO: Implement organization permissions
|
||||
individual_permissions,
|
||||
publicly_accessible,
|
||||
|
|
|
@ -11,6 +11,7 @@ pub mod sharing;
|
|||
|
||||
// Re-export types
|
||||
pub use types::*;
|
||||
pub use types::GetCollectionRequest;
|
||||
|
||||
// Re-export handlers
|
||||
pub use add_assets_to_collection_handler::{add_assets_to_collection_handler, AssetToAdd, AddAssetsToCollectionResult};
|
||||
|
|
|
@ -90,7 +90,7 @@ pub async fn get_dashboard_handler(
|
|||
|
||||
// Check if user has permission to view the dashboard
|
||||
// Users need at least CanView permission or any higher permission
|
||||
let has_permission = check_permission_access(
|
||||
if !check_permission_access(
|
||||
dashboard_with_permission.permission,
|
||||
&[
|
||||
AssetPermissionRole::CanView,
|
||||
|
@ -100,12 +100,15 @@ pub async fn get_dashboard_handler(
|
|||
],
|
||||
dashboard_with_permission.dashboard_file.organization_id,
|
||||
&user.organizations,
|
||||
);
|
||||
|
||||
if !has_permission {
|
||||
) {
|
||||
return Err(anyhow!("You don't have permission to view this dashboard"));
|
||||
}
|
||||
|
||||
// Extract permission for consistent use in response
|
||||
// If the asset is public and the user has no direct permission, default to CanView
|
||||
let permission = dashboard_with_permission.permission
|
||||
.unwrap_or(AssetPermissionRole::CanView);
|
||||
|
||||
let mut conn = match get_pg_pool().get().await {
|
||||
Ok(conn) => conn,
|
||||
Err(e) => return Err(anyhow!("Failed to get database connection: {}", e)),
|
||||
|
@ -282,14 +285,10 @@ pub async fn get_dashboard_handler(
|
|||
};
|
||||
|
||||
Ok(BusterDashboardResponse {
|
||||
access: dashboard_with_permission
|
||||
.permission
|
||||
.unwrap_or(AssetPermissionRole::Owner),
|
||||
access: permission,
|
||||
metrics,
|
||||
dashboard,
|
||||
permission: dashboard_with_permission
|
||||
.permission
|
||||
.unwrap_or(AssetPermissionRole::Owner),
|
||||
permission,
|
||||
public_password: None,
|
||||
collections, // Now populated with associated collections
|
||||
// New sharing fields
|
||||
|
|
|
@ -88,36 +88,33 @@ pub async fn get_metric_handler(
|
|||
.await
|
||||
.map_err(|e| anyhow!("Failed to fetch metric file with permissions: {}", e))?;
|
||||
|
||||
let metric_file = if let Some(metric_file) = metric_file_with_permission {
|
||||
let metric_file_with_permission = if let Some(metric_file) = metric_file_with_permission {
|
||||
metric_file
|
||||
} else {
|
||||
return Err(anyhow!("Metric file not found"));
|
||||
};
|
||||
|
||||
println!("metric_file: {:?}", metric_file.permission);
|
||||
|
||||
// 2. Check if user has at least FullAccess permission
|
||||
// 2. Check if user has at least CanView permission
|
||||
if !check_permission_access(
|
||||
metric_file.permission,
|
||||
metric_file_with_permission.permission,
|
||||
&[
|
||||
AssetPermissionRole::FullAccess,
|
||||
AssetPermissionRole::Owner,
|
||||
AssetPermissionRole::CanEdit,
|
||||
AssetPermissionRole::CanView,
|
||||
],
|
||||
metric_file.metric_file.organization_id,
|
||||
metric_file_with_permission.metric_file.organization_id,
|
||||
&user.organizations,
|
||||
) {
|
||||
return Err(anyhow!("You don't have permission to view this metric"));
|
||||
}
|
||||
|
||||
let permission = if let Some(permission) = metric_file.permission {
|
||||
permission
|
||||
} else {
|
||||
return Err(anyhow!("You don't have permission to view this metric"));
|
||||
};
|
||||
// 3. Extract permission for consistent use in response
|
||||
// If the asset is public and the user has no direct permission, default to CanView
|
||||
let permission = metric_file_with_permission.permission
|
||||
.unwrap_or(AssetPermissionRole::CanView);
|
||||
|
||||
let metric_file = metric_file.metric_file;
|
||||
let metric_file = metric_file_with_permission.metric_file;
|
||||
|
||||
// Map evaluation score to High/Moderate/Low
|
||||
let evaluation_score = metric_file.evaluation_score.map(|score| {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
// Test modules
|
||||
pub mod permission_field_test;
|
|
@ -0,0 +1,227 @@
|
|||
use anyhow::Result;
|
||||
use database::enums::{AssetPermissionRole, AssetType, IdentityType};
|
||||
use database::models::{AssetPermission, Collection};
|
||||
use database::pool::get_pg_pool;
|
||||
use database::schema::{asset_permissions, collections};
|
||||
use database::models::UserToOrganization;
|
||||
use diesel::prelude::*;
|
||||
use diesel_async::RunQueryDsl;
|
||||
use handlers::collections::{get_collection_handler, GetCollectionRequest};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Helper function to create a test collection
|
||||
async fn create_test_collection(
|
||||
organization_id: Uuid,
|
||||
user_id: Uuid,
|
||||
name: &str,
|
||||
) -> Result<Collection> {
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
let collection_id = Uuid::new_v4();
|
||||
|
||||
let collection = Collection {
|
||||
id: collection_id,
|
||||
name: name.to_string(),
|
||||
description: Some(format!("Test collection description for {}", name)),
|
||||
created_by: user_id,
|
||||
updated_by: user_id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
organization_id,
|
||||
};
|
||||
|
||||
diesel::insert_into(collections::table)
|
||||
.values(&collection)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
Ok(collection)
|
||||
}
|
||||
|
||||
/// Helper function to add permission for a collection
|
||||
async fn add_permission(
|
||||
asset_id: Uuid,
|
||||
user_id: Uuid,
|
||||
role: AssetPermissionRole,
|
||||
created_by: Uuid,
|
||||
) -> Result<()> {
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
|
||||
let permission = AssetPermission {
|
||||
identity_id: user_id,
|
||||
identity_type: IdentityType::User,
|
||||
asset_id,
|
||||
asset_type: AssetType::Collection,
|
||||
role,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by,
|
||||
updated_by: created_by,
|
||||
};
|
||||
|
||||
diesel::insert_into(asset_permissions::table)
|
||||
.values(&permission)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test to ensure permission field in collection response matches the permission used for access control
|
||||
#[tokio::test]
|
||||
async fn test_collection_permission_field_consistency() -> Result<()> {
|
||||
// Create user and organization for testing
|
||||
let user_id = Uuid::new_v4();
|
||||
let org_id = Uuid::new_v4();
|
||||
|
||||
// Create test collection
|
||||
let collection = create_test_collection(
|
||||
org_id,
|
||||
user_id,
|
||||
"Test Permission Collection"
|
||||
).await?;
|
||||
|
||||
// Add permission for asset
|
||||
add_permission(
|
||||
collection.id,
|
||||
user_id,
|
||||
AssetPermissionRole::Owner,
|
||||
user_id
|
||||
).await?;
|
||||
|
||||
// Create middleware user
|
||||
let middleware_user = middleware::AuthenticatedUser {
|
||||
id: user_id,
|
||||
email: "test@example.com".to_string(),
|
||||
name: Some("Test User".to_string()),
|
||||
config: serde_json::json!({}),
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
attributes: serde_json::json!({}),
|
||||
avatar_url: None,
|
||||
organizations: vec![
|
||||
middleware::OrganizationMembership {
|
||||
id: org_id,
|
||||
role: database::enums::UserOrganizationRole::WorkspaceAdmin,
|
||||
},
|
||||
],
|
||||
teams: vec![],
|
||||
};
|
||||
|
||||
// Create request data
|
||||
let request = GetCollectionRequest {
|
||||
id: collection.id,
|
||||
};
|
||||
|
||||
// Get collection with the user who has owner permission
|
||||
let collection_response = get_collection_handler(&middleware_user, request).await?;
|
||||
|
||||
// Check if permission field matches what we set
|
||||
assert_eq!(collection_response.permission, AssetPermissionRole::Owner);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test to ensure access is denied for users without permissions
|
||||
#[tokio::test]
|
||||
async fn test_collection_permission_denied() -> Result<()> {
|
||||
// Create user and organization for testing
|
||||
let owner_id = Uuid::new_v4();
|
||||
let org_id = Uuid::new_v4();
|
||||
|
||||
// Create a private test collection
|
||||
let collection = create_test_collection(
|
||||
org_id,
|
||||
owner_id,
|
||||
"Private Collection"
|
||||
).await?;
|
||||
|
||||
// Create another user in a different organization
|
||||
let other_user_id = Uuid::new_v4();
|
||||
let other_org_id = Uuid::new_v4();
|
||||
|
||||
// Create middleware user for other user
|
||||
let other_middleware_user = middleware::AuthenticatedUser {
|
||||
id: other_user_id,
|
||||
email: "other@example.com".to_string(),
|
||||
name: Some("Other User".to_string()),
|
||||
config: serde_json::json!({}),
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
attributes: serde_json::json!({}),
|
||||
avatar_url: None,
|
||||
organizations: vec![
|
||||
middleware::OrganizationMembership {
|
||||
id: other_org_id,
|
||||
role: database::enums::UserOrganizationRole::Viewer,
|
||||
},
|
||||
],
|
||||
teams: vec![],
|
||||
};
|
||||
|
||||
// Create request data
|
||||
let request = GetCollectionRequest {
|
||||
id: collection.id,
|
||||
};
|
||||
|
||||
// Try to get collection with a user who has no permissions
|
||||
let result = get_collection_handler(&other_middleware_user, request).await;
|
||||
|
||||
// Should be denied access
|
||||
assert!(result.is_err());
|
||||
let error = result.unwrap_err();
|
||||
assert!(error.to_string().contains("You don't have permission"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test inherited permissions through organization role
|
||||
#[tokio::test]
|
||||
async fn test_collection_org_admin_permission() -> Result<()> {
|
||||
// Create user and organization for testing
|
||||
let user_id = Uuid::new_v4();
|
||||
let org_id = Uuid::new_v4();
|
||||
|
||||
// Create test collection (without direct permission)
|
||||
let collection = create_test_collection(
|
||||
org_id,
|
||||
user_id,
|
||||
"Admin Collection"
|
||||
).await?;
|
||||
|
||||
// Create middleware user with admin role
|
||||
let middleware_user = middleware::AuthenticatedUser {
|
||||
id: user_id,
|
||||
email: "admin@example.com".to_string(),
|
||||
name: Some("Admin User".to_string()),
|
||||
config: serde_json::json!({}),
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
attributes: serde_json::json!({}),
|
||||
avatar_url: None,
|
||||
organizations: vec![
|
||||
middleware::OrganizationMembership {
|
||||
id: org_id,
|
||||
role: database::enums::UserOrganizationRole::WorkspaceAdmin,
|
||||
},
|
||||
],
|
||||
teams: vec![],
|
||||
};
|
||||
|
||||
// Create request data
|
||||
let request = GetCollectionRequest {
|
||||
id: collection.id,
|
||||
};
|
||||
|
||||
// Get collection with the admin user
|
||||
let result = get_collection_handler(&middleware_user, request).await;
|
||||
|
||||
// Access should be successful and admin should have Owner permission
|
||||
assert!(result.is_ok(), "Admin should have access: {:?}", result.err());
|
||||
if let Ok(response) = result {
|
||||
assert_eq!(response.permission, AssetPermissionRole::Owner);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
mod list_sharing_test;
|
||||
mod list_sharing_test;
|
||||
mod permission_field_test;
|
|
@ -0,0 +1,265 @@
|
|||
use anyhow::Result;
|
||||
use database::enums::{AssetPermissionRole, AssetType, IdentityType};
|
||||
use database::models::{AssetPermission, DashboardFile};
|
||||
use database::pool::get_pg_pool;
|
||||
use database::schema::{asset_permissions, dashboard_files};
|
||||
use database::models::UserToOrganization;
|
||||
use database::types::{DashboardYml, VersionHistory};
|
||||
use diesel::prelude::*;
|
||||
use diesel_async::RunQueryDsl;
|
||||
use handlers::dashboards::get_dashboard_handler;
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Helper function to create a test dashboard file
|
||||
async fn create_test_dashboard(
|
||||
organization_id: Uuid,
|
||||
user_id: Uuid,
|
||||
name: &str,
|
||||
) -> Result<DashboardFile> {
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
let dashboard_id = Uuid::new_v4();
|
||||
|
||||
// Create a simple dashboard content
|
||||
let content = DashboardYml {
|
||||
name: name.to_string(),
|
||||
description: Some(format!("Test dashboard description for {}", name)),
|
||||
rows: Vec::new(),
|
||||
};
|
||||
|
||||
let dashboard_file = DashboardFile {
|
||||
id: dashboard_id,
|
||||
name: name.to_string(),
|
||||
file_name: format!("{}.yml", name.to_lowercase().replace(" ", "_")),
|
||||
content,
|
||||
filter: None,
|
||||
organization_id,
|
||||
created_by: user_id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
publicly_accessible: false,
|
||||
publicly_enabled_by: None,
|
||||
public_expiry_date: None,
|
||||
version_history: VersionHistory(HashMap::new()),
|
||||
public_password: None,
|
||||
};
|
||||
|
||||
diesel::insert_into(dashboard_files::table)
|
||||
.values(&dashboard_file)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
Ok(dashboard_file)
|
||||
}
|
||||
|
||||
/// Helper function to add permission for a dashboard
|
||||
async fn add_permission(
|
||||
asset_id: Uuid,
|
||||
user_id: Uuid,
|
||||
role: AssetPermissionRole,
|
||||
created_by: Uuid,
|
||||
) -> Result<()> {
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
|
||||
let permission = AssetPermission {
|
||||
identity_id: user_id,
|
||||
identity_type: IdentityType::User,
|
||||
asset_id,
|
||||
asset_type: AssetType::DashboardFile,
|
||||
role,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by,
|
||||
updated_by: created_by,
|
||||
};
|
||||
|
||||
diesel::insert_into(asset_permissions::table)
|
||||
.values(&permission)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test to ensure permission fields in dashboard response match the permission used for access control
|
||||
#[tokio::test]
|
||||
async fn test_dashboard_permission_field_consistency() -> Result<()> {
|
||||
// Create user and organization for testing
|
||||
let user_id = Uuid::new_v4();
|
||||
let org_id = Uuid::new_v4();
|
||||
|
||||
// Create test dashboard
|
||||
let dashboard = create_test_dashboard(
|
||||
org_id,
|
||||
user_id,
|
||||
"Test Permission Dashboard"
|
||||
).await?;
|
||||
|
||||
// Add permission for asset
|
||||
add_permission(
|
||||
dashboard.id,
|
||||
user_id,
|
||||
AssetPermissionRole::Owner,
|
||||
user_id
|
||||
).await?;
|
||||
|
||||
// Create middleware user
|
||||
let middleware_user = middleware::AuthenticatedUser {
|
||||
id: user_id,
|
||||
email: "test@example.com".to_string(),
|
||||
name: Some("Test User".to_string()),
|
||||
config: serde_json::json!({}),
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
attributes: serde_json::json!({}),
|
||||
avatar_url: None,
|
||||
organizations: vec![
|
||||
middleware::OrganizationMembership {
|
||||
id: org_id,
|
||||
role: database::enums::UserOrganizationRole::WorkspaceAdmin,
|
||||
},
|
||||
],
|
||||
teams: vec![],
|
||||
};
|
||||
|
||||
// Get dashboard with the user who has owner permission
|
||||
let dashboard_response = get_dashboard_handler(&dashboard.id, &middleware_user, None).await?;
|
||||
|
||||
// Check if permission fields are consistent
|
||||
assert_eq!(dashboard_response.permission, AssetPermissionRole::Owner);
|
||||
assert_eq!(dashboard_response.access, AssetPermissionRole::Owner);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test to ensure public dashboards grant CanView permission to users without direct permissions
|
||||
#[tokio::test]
|
||||
async fn test_public_dashboard_permission_field() -> Result<()> {
|
||||
// Create user and organization for testing
|
||||
let owner_id = Uuid::new_v4();
|
||||
let org_id = Uuid::new_v4();
|
||||
|
||||
// Create test dashboard
|
||||
let dashboard = create_test_dashboard(
|
||||
org_id,
|
||||
owner_id,
|
||||
"Public Dashboard"
|
||||
).await?;
|
||||
|
||||
// Make dashboard public
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
diesel::update(dashboard_files::table)
|
||||
.filter(dashboard_files::id.eq(dashboard.id))
|
||||
.set((
|
||||
dashboard_files::publicly_accessible.eq(true),
|
||||
dashboard_files::publicly_enabled_by.eq(Some(owner_id)),
|
||||
dashboard_files::public_expiry_date.eq(Some(chrono::Utc::now() + chrono::Duration::days(7))),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Create another user
|
||||
let other_user_id = Uuid::new_v4();
|
||||
|
||||
// Add user to organization with viewer role
|
||||
let user_org = UserToOrganization {
|
||||
user_id: other_user_id,
|
||||
organization_id: org_id,
|
||||
role: database::enums::UserOrganizationRole::Viewer,
|
||||
sharing_setting: database::enums::SharingSetting::None,
|
||||
edit_sql: true,
|
||||
upload_csv: true,
|
||||
export_assets: true,
|
||||
email_slack_enabled: true,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by: owner_id,
|
||||
updated_by: owner_id,
|
||||
deleted_by: None,
|
||||
status: database::enums::UserOrganizationStatus::Active,
|
||||
};
|
||||
|
||||
diesel::insert_into(database::schema::users_to_organizations::table)
|
||||
.values(&user_org)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Create middleware user for other user
|
||||
let other_middleware_user = middleware::AuthenticatedUser {
|
||||
id: other_user_id,
|
||||
email: "other@example.com".to_string(),
|
||||
name: Some("Other User".to_string()),
|
||||
config: serde_json::json!({}),
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
attributes: serde_json::json!({}),
|
||||
avatar_url: None,
|
||||
organizations: vec![
|
||||
middleware::OrganizationMembership {
|
||||
id: org_id,
|
||||
role: database::enums::UserOrganizationRole::Viewer,
|
||||
},
|
||||
],
|
||||
teams: vec![],
|
||||
};
|
||||
|
||||
// Get dashboard with the other user who doesn't have direct permission
|
||||
let dashboard_response = get_dashboard_handler(&dashboard.id, &other_middleware_user, None).await?;
|
||||
|
||||
// Public assets should have CanView permission by default
|
||||
assert_eq!(dashboard_response.permission, AssetPermissionRole::CanView);
|
||||
assert_eq!(dashboard_response.access, AssetPermissionRole::CanView);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test to ensure access is denied for users without permissions and non-public dashboards
|
||||
#[tokio::test]
|
||||
async fn test_dashboard_permission_denied() -> Result<()> {
|
||||
// Create user and organization for testing
|
||||
let owner_id = Uuid::new_v4();
|
||||
let org_id = Uuid::new_v4();
|
||||
|
||||
// Create a private test dashboard
|
||||
let dashboard = create_test_dashboard(
|
||||
org_id,
|
||||
owner_id,
|
||||
"Private Dashboard"
|
||||
).await?;
|
||||
|
||||
// Create another user in a different organization
|
||||
let other_user_id = Uuid::new_v4();
|
||||
let other_org_id = Uuid::new_v4();
|
||||
|
||||
// Create middleware user for other user
|
||||
let other_middleware_user = middleware::AuthenticatedUser {
|
||||
id: other_user_id,
|
||||
email: "other@example.com".to_string(),
|
||||
name: Some("Other User".to_string()),
|
||||
config: serde_json::json!({}),
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
attributes: serde_json::json!({}),
|
||||
avatar_url: None,
|
||||
organizations: vec![
|
||||
middleware::OrganizationMembership {
|
||||
id: other_org_id,
|
||||
role: database::enums::UserOrganizationRole::Viewer,
|
||||
},
|
||||
],
|
||||
teams: vec![],
|
||||
};
|
||||
|
||||
// Try to get dashboard with a user who has no permissions
|
||||
let result = get_dashboard_handler(&dashboard.id, &other_middleware_user, None).await;
|
||||
|
||||
// Should be denied access
|
||||
assert!(result.is_err());
|
||||
let error = result.unwrap_err();
|
||||
assert!(error.to_string().contains("You don't have permission"));
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
// Test modules
|
||||
pub mod permission_field_test;
|
|
@ -0,0 +1,298 @@
|
|||
use anyhow::Result;
|
||||
use database::enums::{AssetPermissionRole, AssetType, IdentityType};
|
||||
use database::models::{AssetPermission, MetricFile};
|
||||
use database::pool::get_pg_pool;
|
||||
use database::schema::{asset_permissions, metric_files};
|
||||
use database::models::UserToOrganization;
|
||||
use database::types::metric_yml::{BarAndLineAxis, BarLineChartConfig, BaseChartConfig};
|
||||
use database::types::{ChartConfig, MetricYml, VersionHistory};
|
||||
use diesel::prelude::*;
|
||||
use diesel_async::RunQueryDsl;
|
||||
use handlers::metrics::get_metric_handler;
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Helper function to create a test metric file
|
||||
async fn create_test_metric(
|
||||
organization_id: Uuid,
|
||||
user_id: Uuid,
|
||||
name: &str,
|
||||
) -> Result<MetricFile> {
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
let metric_id = Uuid::new_v4();
|
||||
|
||||
// Create a simple metric content
|
||||
let content = MetricYml {
|
||||
name: name.to_string(),
|
||||
description: Some(format!("Test metric description for {}", name)),
|
||||
sql: "SELECT * FROM test".to_string(),
|
||||
time_frame: "last 30 days".to_string(),
|
||||
chart_config: ChartConfig::Bar(BarLineChartConfig {
|
||||
base: BaseChartConfig {
|
||||
column_label_formats: indexmap::IndexMap::new(),
|
||||
column_settings: None,
|
||||
colors: None,
|
||||
show_legend: None,
|
||||
grid_lines: None,
|
||||
show_legend_headline: None,
|
||||
goal_lines: None,
|
||||
trendlines: None,
|
||||
disable_tooltip: None,
|
||||
y_axis_config: None,
|
||||
x_axis_config: None,
|
||||
category_axis_style_config: None,
|
||||
y2_axis_config: None,
|
||||
},
|
||||
bar_and_line_axis: BarAndLineAxis {
|
||||
x: vec!["x".to_string()],
|
||||
y: vec!["y".to_string()],
|
||||
category: None,
|
||||
tooltip: None,
|
||||
},
|
||||
bar_layout: None,
|
||||
bar_sort_by: None,
|
||||
bar_group_type: None,
|
||||
bar_show_total_at_top: None,
|
||||
line_group_type: None,
|
||||
}),
|
||||
dataset_ids: Vec::new(),
|
||||
};
|
||||
|
||||
let metric_file = MetricFile {
|
||||
id: metric_id,
|
||||
name: name.to_string(),
|
||||
file_name: format!("{}.yml", name.to_lowercase().replace(" ", "_")),
|
||||
content,
|
||||
verification: database::enums::Verification::Verified,
|
||||
evaluation_obj: None,
|
||||
evaluation_summary: None,
|
||||
evaluation_score: None,
|
||||
organization_id,
|
||||
created_by: user_id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
publicly_accessible: false,
|
||||
publicly_enabled_by: None,
|
||||
public_expiry_date: None,
|
||||
version_history: VersionHistory(HashMap::new()),
|
||||
data_metadata: None,
|
||||
public_password: None,
|
||||
};
|
||||
|
||||
diesel::insert_into(metric_files::table)
|
||||
.values(&metric_file)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
Ok(metric_file)
|
||||
}
|
||||
|
||||
/// Helper function to add permission for a metric
|
||||
async fn add_permission(
|
||||
asset_id: Uuid,
|
||||
user_id: Uuid,
|
||||
role: AssetPermissionRole,
|
||||
created_by: Uuid,
|
||||
) -> Result<()> {
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
|
||||
let permission = AssetPermission {
|
||||
identity_id: user_id,
|
||||
identity_type: IdentityType::User,
|
||||
asset_id,
|
||||
asset_type: AssetType::MetricFile,
|
||||
role,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by,
|
||||
updated_by: created_by,
|
||||
};
|
||||
|
||||
diesel::insert_into(asset_permissions::table)
|
||||
.values(&permission)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test to ensure permission field in the response matches the permission used for access control
|
||||
#[tokio::test]
|
||||
async fn test_metric_permission_field_consistency() -> Result<()> {
|
||||
// Create user and organization for testing
|
||||
let user_id = Uuid::new_v4();
|
||||
let org_id = Uuid::new_v4();
|
||||
|
||||
// Create test metric
|
||||
let metric = create_test_metric(
|
||||
org_id,
|
||||
user_id,
|
||||
"Test Permission Metric"
|
||||
).await?;
|
||||
|
||||
// Add permission for asset
|
||||
add_permission(
|
||||
metric.id,
|
||||
user_id,
|
||||
AssetPermissionRole::Owner,
|
||||
user_id
|
||||
).await?;
|
||||
|
||||
// Create middleware user
|
||||
let middleware_user = middleware::AuthenticatedUser {
|
||||
id: user_id,
|
||||
email: "test@example.com".to_string(),
|
||||
name: Some("Test User".to_string()),
|
||||
config: serde_json::json!({}),
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
attributes: serde_json::json!({}),
|
||||
avatar_url: None,
|
||||
organizations: vec![
|
||||
middleware::OrganizationMembership {
|
||||
id: org_id,
|
||||
role: database::enums::UserOrganizationRole::WorkspaceAdmin,
|
||||
},
|
||||
],
|
||||
teams: vec![],
|
||||
};
|
||||
|
||||
// Get metric with the user who has owner permission
|
||||
let metric_response = get_metric_handler(&metric.id, &middleware_user, None).await?;
|
||||
|
||||
// Check if permission field matches what we set
|
||||
assert_eq!(metric_response.permission, AssetPermissionRole::Owner);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test to ensure public metrics grant CanView permission to users without direct permissions
|
||||
#[tokio::test]
|
||||
async fn test_public_metric_permission_field() -> Result<()> {
|
||||
// Create user and organization for testing
|
||||
let owner_id = Uuid::new_v4();
|
||||
let org_id = Uuid::new_v4();
|
||||
|
||||
// Create test metric
|
||||
let metric = create_test_metric(
|
||||
org_id,
|
||||
owner_id,
|
||||
"Public Metric"
|
||||
).await?;
|
||||
|
||||
// Make metric public
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
diesel::update(metric_files::table)
|
||||
.filter(metric_files::id.eq(metric.id))
|
||||
.set((
|
||||
metric_files::publicly_accessible.eq(true),
|
||||
metric_files::publicly_enabled_by.eq(Some(owner_id)),
|
||||
metric_files::public_expiry_date.eq(Some(chrono::Utc::now() + chrono::Duration::days(7))),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Create another user
|
||||
let other_user_id = Uuid::new_v4();
|
||||
|
||||
// Add user to organization with viewer role
|
||||
let user_org = UserToOrganization {
|
||||
user_id: other_user_id,
|
||||
organization_id: org_id,
|
||||
role: database::enums::UserOrganizationRole::Viewer,
|
||||
sharing_setting: database::enums::SharingSetting::None,
|
||||
edit_sql: true,
|
||||
upload_csv: true,
|
||||
export_assets: true,
|
||||
email_slack_enabled: true,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by: owner_id,
|
||||
updated_by: owner_id,
|
||||
deleted_by: None,
|
||||
status: database::enums::UserOrganizationStatus::Active,
|
||||
};
|
||||
|
||||
diesel::insert_into(database::schema::users_to_organizations::table)
|
||||
.values(&user_org)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Create middleware user for other user
|
||||
let other_middleware_user = middleware::AuthenticatedUser {
|
||||
id: other_user_id,
|
||||
email: "other@example.com".to_string(),
|
||||
name: Some("Other User".to_string()),
|
||||
config: serde_json::json!({}),
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
attributes: serde_json::json!({}),
|
||||
avatar_url: None,
|
||||
organizations: vec![
|
||||
middleware::OrganizationMembership {
|
||||
id: org_id,
|
||||
role: database::enums::UserOrganizationRole::Viewer,
|
||||
},
|
||||
],
|
||||
teams: vec![],
|
||||
};
|
||||
|
||||
// Get metric with the other user who doesn't have direct permission
|
||||
let metric_response = get_metric_handler(&metric.id, &other_middleware_user, None).await?;
|
||||
|
||||
// Public assets should have CanView permission by default
|
||||
assert_eq!(metric_response.permission, AssetPermissionRole::CanView);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test to ensure access is denied for users without permissions and non-public metrics
|
||||
#[tokio::test]
|
||||
async fn test_metric_permission_denied() -> Result<()> {
|
||||
// Create user and organization for testing
|
||||
let owner_id = Uuid::new_v4();
|
||||
let org_id = Uuid::new_v4();
|
||||
|
||||
// Create a private test metric
|
||||
let metric = create_test_metric(
|
||||
org_id,
|
||||
owner_id,
|
||||
"Private Metric"
|
||||
).await?;
|
||||
|
||||
// Create another user in a different organization
|
||||
let other_user_id = Uuid::new_v4();
|
||||
let other_org_id = Uuid::new_v4();
|
||||
|
||||
// Create middleware user for other user
|
||||
let other_middleware_user = middleware::AuthenticatedUser {
|
||||
id: other_user_id,
|
||||
email: "other@example.com".to_string(),
|
||||
name: Some("Other User".to_string()),
|
||||
config: serde_json::json!({}),
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
attributes: serde_json::json!({}),
|
||||
avatar_url: None,
|
||||
organizations: vec![
|
||||
middleware::OrganizationMembership {
|
||||
id: other_org_id,
|
||||
role: database::enums::UserOrganizationRole::Viewer,
|
||||
},
|
||||
],
|
||||
teams: vec![],
|
||||
};
|
||||
|
||||
// Try to get metric with a user who has no permissions
|
||||
let result = get_metric_handler(&metric.id, &other_middleware_user, None).await;
|
||||
|
||||
// Should be denied access
|
||||
assert!(result.is_err());
|
||||
let error = result.unwrap_err();
|
||||
assert!(error.to_string().contains("You don't have permission"));
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -30,3 +30,5 @@ fn init_test_env() {
|
|||
|
||||
// Test modules
|
||||
pub mod dashboards;
|
||||
pub mod metrics;
|
||||
pub mod collections;
|
||||
|
|
Loading…
Reference in New Issue