get max permissions between direct and workspace

This commit is contained in:
dal 2025-07-17 13:14:57 -06:00
parent a0a1e11493
commit 955aab3232
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
7 changed files with 135 additions and 69 deletions

View File

@ -92,6 +92,8 @@ pub enum TeamToUserRole {
Hash,
diesel::AsExpression,
diesel::FromSqlRow,
Ord,
PartialOrd,
)]
#[diesel(sql_type = sql_types::AssetPermissionRoleEnum)]
#[serde(rename_all = "camelCase")]

View File

@ -12,7 +12,7 @@ use database::{
use diesel::{ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl, Queryable};
use diesel_async::RunQueryDsl;
use middleware::AuthenticatedUser;
use sharing::check_permission_access;
use sharing::{check_permission_access, compute_effective_permission};
use tracing;
use uuid::Uuid;
@ -96,22 +96,22 @@ pub async fn get_collection_handler(
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 mut permission = collection_with_permission.permission
.unwrap_or(AssetPermissionRole::CanView);
// Compute the effective permission (highest of direct and workspace sharing)
let permission = compute_effective_permission(
collection_with_permission.permission,
collection_with_permission.collection.workspace_sharing,
collection_with_permission.collection.organization_id,
&user.organizations,
).unwrap_or(AssetPermissionRole::CanView);
// Check if user is WorkspaceAdmin or DataAdmin for this organization
let is_admin = user.organizations.iter().any(|org| {
org.id == collection_with_permission.collection.organization_id
&& (org.role == database::enums::UserOrganizationRole::WorkspaceAdmin
|| org.role == database::enums::UserOrganizationRole::DataAdmin)
});
if is_admin {
// Admin users get Owner permissions
permission = AssetPermissionRole::Owner;
}
tracing::debug!(
collection_id = %req.id,
user_id = %user.id,
direct_permission = ?collection_with_permission.permission,
workspace_sharing = ?collection_with_permission.collection.workspace_sharing,
effective_permission = ?permission,
"Computed effective permission for collection"
);
let mut conn = match get_pg_pool().get().await {
Ok(conn) => conn,

View File

@ -21,7 +21,7 @@ use database::schema::{
asset_permissions, collections, collections_to_assets, dashboard_files, metric_files, users,
};
use database::types::{MetricYml, VersionHistory};
use sharing::check_permission_access;
use sharing::{check_permission_access, compute_effective_permission};
use super::{
BusterDashboard, BusterDashboardResponse, DashboardConfig, DashboardRow, DashboardRowItem,
@ -131,22 +131,23 @@ pub async fn get_dashboard_handler(
tracing::debug!(dashboard_id = %dashboard_id, ?direct_permission_level, has_sufficient_direct_permission, "Direct permission check result");
if has_sufficient_direct_permission {
// Check if user is WorkspaceAdmin or DataAdmin for this organization
let is_admin = user.organizations.iter().any(|org| {
org.id == dashboard_file.organization_id
&& (org.role == database::enums::UserOrganizationRole::WorkspaceAdmin
|| org.role == database::enums::UserOrganizationRole::DataAdmin)
});
// Compute the effective permission (highest of direct and workspace sharing)
let effective_permission = compute_effective_permission(
direct_permission_level,
dashboard_file.workspace_sharing,
dashboard_file.organization_id,
&user.organizations,
);
if is_admin {
// Admin users get Owner permissions
permission = AssetPermissionRole::Owner;
tracing::debug!(dashboard_id = %dashboard_id, user_id = %user.id, ?permission, "Granting Owner access to admin user.");
} else {
// User has direct permission, use that role
permission = direct_permission_level.unwrap_or(AssetPermissionRole::CanView); // Default just in case
tracing::debug!(dashboard_id = %dashboard_id, user_id = %user.id, ?permission, "Granting access via direct permission.");
}
permission = effective_permission.unwrap_or(AssetPermissionRole::CanView);
tracing::debug!(
dashboard_id = %dashboard_id,
user_id = %user.id,
?direct_permission_level,
workspace_sharing = ?dashboard_file.workspace_sharing,
?permission,
"Granting access with effective permission (max of direct and workspace sharing)."
);
} else {
// No sufficient direct/admin permission, check if user has access via a chat
tracing::debug!(dashboard_id = %dashboard_id, "Insufficient direct/admin permission. Checking chat access.");

View File

@ -17,7 +17,7 @@ use database::schema::{
asset_permissions, collections, collections_to_assets, dashboard_files, datasets, metric_files,
metric_files_to_dashboard_files, metric_files_to_datasets, users,
};
use sharing::check_permission_access;
use sharing::{check_permission_access, compute_effective_permission};
use super::Version;
@ -142,22 +142,23 @@ pub async fn get_metric_for_dashboard_handler(
tracing::debug!(metric_id = %metric_id, ?direct_permission_level, has_sufficient_direct_permission, "Direct permission check result");
if has_sufficient_direct_permission {
// Check if user is WorkspaceAdmin or DataAdmin for this organization
let is_admin = user.organizations.iter().any(|org| {
org.id == metric_file.organization_id
&& (org.role == database::enums::UserOrganizationRole::WorkspaceAdmin
|| org.role == database::enums::UserOrganizationRole::DataAdmin)
});
// Compute the effective permission (highest of direct and workspace sharing)
let effective_permission = compute_effective_permission(
direct_permission_level,
metric_file.workspace_sharing,
metric_file.organization_id,
&user.organizations,
);
if is_admin {
// Admin users get Owner permissions
permission = AssetPermissionRole::Owner;
tracing::debug!(metric_id = %metric_id, user_id = %user.id, ?permission, "Granting Owner access to admin user.");
} else {
// User has direct permission, use that role
permission = direct_permission_level.unwrap_or(AssetPermissionRole::CanView); // Default just in case
tracing::debug!(metric_id = %metric_id, user_id = %user.id, ?permission, "Granting access via direct permission.");
}
permission = effective_permission.unwrap_or(AssetPermissionRole::CanView);
tracing::debug!(
metric_id = %metric_id,
user_id = %user.id,
?direct_permission_level,
workspace_sharing = ?metric_file.workspace_sharing,
?permission,
"Granting access with effective permission (max of direct and workspace sharing)."
);
} else {
// No sufficient direct/admin permission, check if user has access via a dashboard
tracing::debug!(metric_id = %metric_id, "Insufficient direct/admin permission. Checking dashboard access.");

View File

@ -15,7 +15,7 @@ use database::schema::{
asset_permissions, collections, collections_to_assets, dashboard_files, datasets,
metric_files_to_dashboard_files, users, metric_files_to_datasets,
};
use sharing::check_permission_access;
use sharing::{check_permission_access, compute_effective_permission};
use super::Version;
@ -140,22 +140,23 @@ pub async fn get_metric_handler(
tracing::debug!(metric_id = %metric_id, ?direct_permission_level, has_sufficient_direct_permission, "Direct permission check result");
if has_sufficient_direct_permission {
// Check if user is WorkspaceAdmin or DataAdmin for this organization
let is_admin = user.organizations.iter().any(|org| {
org.id == metric_file.organization_id
&& (org.role == database::enums::UserOrganizationRole::WorkspaceAdmin
|| org.role == database::enums::UserOrganizationRole::DataAdmin)
});
// Compute the effective permission (highest of direct and workspace sharing)
let effective_permission = compute_effective_permission(
direct_permission_level,
metric_file.workspace_sharing,
metric_file.organization_id,
&user.organizations,
);
if is_admin {
// Admin users get Owner permissions
permission = AssetPermissionRole::Owner;
tracing::debug!(metric_id = %metric_id, user_id = %user.id, ?permission, "Granting Owner access to admin user.");
} else {
// User has direct permission, use that role
permission = direct_permission_level.unwrap_or(AssetPermissionRole::CanView); // Default just in case
tracing::debug!(metric_id = %metric_id, user_id = %user.id, ?permission, "Granting access via direct permission.");
}
permission = effective_permission.unwrap_or(AssetPermissionRole::CanView);
tracing::debug!(
metric_id = %metric_id,
user_id = %user.id,
?direct_permission_level,
workspace_sharing = ?metric_file.workspace_sharing,
?permission,
"Granting access with effective permission (max of direct and workspace sharing)."
);
} else {
// No sufficient direct/admin permission, check if user has access via a dashboard
tracing::debug!(metric_id = %metric_id, "Insufficient direct/admin permission. Checking dashboard access.");

View File

@ -5,6 +5,63 @@ use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl, Opti
use diesel_async::RunQueryDsl;
use middleware::OrganizationMembership;
use uuid::Uuid;
use std::cmp::Ordering;
/// Computes the effective permission level for a user on an asset by taking the maximum
/// of their direct permission and workspace sharing permission.
///
/// # Arguments
/// * `direct_permission` - The user's direct permission on the asset (if any)
/// * `workspace_sharing` - The workspace sharing level for the asset
/// * `organization_id` - UUID of the organization
/// * `organization_role_grants` - User's organization memberships
///
/// # Returns
/// * `Option<AssetPermissionRole>` - The highest permission level available to the user
pub fn compute_effective_permission(
direct_permission: Option<AssetPermissionRole>,
workspace_sharing: WorkspaceSharing,
organization_id: Uuid,
organization_role_grants: &[OrganizationMembership],
) -> Option<AssetPermissionRole> {
// First check if the user has WorkspaceAdmin or DataAdmin role for the organization
for org in organization_role_grants {
if org.id == organization_id
&& (org.role == UserOrganizationRole::WorkspaceAdmin
|| org.role == UserOrganizationRole::DataAdmin)
{
return Some(AssetPermissionRole::Owner);
}
}
// Compute workspace-granted permission
let workspace_permission = if workspace_sharing != WorkspaceSharing::None {
// Check if user is member of the organization
if organization_role_grants.iter().any(|org| org.id == organization_id) {
match workspace_sharing {
WorkspaceSharing::CanView => Some(AssetPermissionRole::CanView),
WorkspaceSharing::CanEdit => Some(AssetPermissionRole::CanEdit),
WorkspaceSharing::FullAccess => Some(AssetPermissionRole::FullAccess),
WorkspaceSharing::None => None,
}
} else {
None
}
} else {
None
};
// Return the highest permission level
match (direct_permission, workspace_permission) {
(Some(direct), Some(workspace)) => {
// Use the max method to get the higher permission
Some(direct.max(workspace))
}
(Some(direct), None) => Some(direct),
(None, Some(workspace)) => Some(workspace),
(None, None) => None,
}
}
/// Checks if a user has sufficient permissions based on organization roles and asset permissions.
///

View File

@ -19,4 +19,8 @@ pub use types::{
SerializableAssetPermission, UserInfo,
};
pub use user_lookup::find_user_by_email;
pub use asset_access_checks::{check_permission_access, check_metric_dashboard_access, check_metric_chat_access, check_dashboard_chat_access};
pub use asset_access_checks::{
check_permission_access, check_metric_dashboard_access, check_metric_chat_access,
check_dashboard_chat_access, check_metric_collection_access, check_dashboard_collection_access,
compute_effective_permission
};