mirror of https://github.com/buster-so/buster.git
get max permissions between direct and workspace
This commit is contained in:
parent
a0a1e11493
commit
955aab3232
|
@ -92,6 +92,8 @@ pub enum TeamToUserRole {
|
||||||
Hash,
|
Hash,
|
||||||
diesel::AsExpression,
|
diesel::AsExpression,
|
||||||
diesel::FromSqlRow,
|
diesel::FromSqlRow,
|
||||||
|
Ord,
|
||||||
|
PartialOrd,
|
||||||
)]
|
)]
|
||||||
#[diesel(sql_type = sql_types::AssetPermissionRoleEnum)]
|
#[diesel(sql_type = sql_types::AssetPermissionRoleEnum)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|
|
@ -12,7 +12,7 @@ use database::{
|
||||||
use diesel::{ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl, Queryable};
|
use diesel::{ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl, Queryable};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use middleware::AuthenticatedUser;
|
use middleware::AuthenticatedUser;
|
||||||
use sharing::check_permission_access;
|
use sharing::{check_permission_access, compute_effective_permission};
|
||||||
use tracing;
|
use tracing;
|
||||||
use uuid::Uuid;
|
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"));
|
return Err(anyhow!("You don't have permission to view this collection"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract permission for consistent use in response
|
// Compute the effective permission (highest of direct and workspace sharing)
|
||||||
// If the asset is public and the user has no direct permission, default to CanView
|
let permission = compute_effective_permission(
|
||||||
let mut permission = collection_with_permission.permission
|
collection_with_permission.permission,
|
||||||
.unwrap_or(AssetPermissionRole::CanView);
|
collection_with_permission.collection.workspace_sharing,
|
||||||
|
collection_with_permission.collection.organization_id,
|
||||||
// Check if user is WorkspaceAdmin or DataAdmin for this organization
|
&user.organizations,
|
||||||
let is_admin = user.organizations.iter().any(|org| {
|
).unwrap_or(AssetPermissionRole::CanView);
|
||||||
org.id == collection_with_permission.collection.organization_id
|
|
||||||
&& (org.role == database::enums::UserOrganizationRole::WorkspaceAdmin
|
tracing::debug!(
|
||||||
|| org.role == database::enums::UserOrganizationRole::DataAdmin)
|
collection_id = %req.id,
|
||||||
});
|
user_id = %user.id,
|
||||||
|
direct_permission = ?collection_with_permission.permission,
|
||||||
if is_admin {
|
workspace_sharing = ?collection_with_permission.collection.workspace_sharing,
|
||||||
// Admin users get Owner permissions
|
effective_permission = ?permission,
|
||||||
permission = AssetPermissionRole::Owner;
|
"Computed effective permission for collection"
|
||||||
}
|
);
|
||||||
|
|
||||||
let mut conn = match get_pg_pool().get().await {
|
let mut conn = match get_pg_pool().get().await {
|
||||||
Ok(conn) => conn,
|
Ok(conn) => conn,
|
||||||
|
|
|
@ -21,7 +21,7 @@ use database::schema::{
|
||||||
asset_permissions, collections, collections_to_assets, dashboard_files, metric_files, users,
|
asset_permissions, collections, collections_to_assets, dashboard_files, metric_files, users,
|
||||||
};
|
};
|
||||||
use database::types::{MetricYml, VersionHistory};
|
use database::types::{MetricYml, VersionHistory};
|
||||||
use sharing::check_permission_access;
|
use sharing::{check_permission_access, compute_effective_permission};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
BusterDashboard, BusterDashboardResponse, DashboardConfig, DashboardRow, DashboardRowItem,
|
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");
|
tracing::debug!(dashboard_id = %dashboard_id, ?direct_permission_level, has_sufficient_direct_permission, "Direct permission check result");
|
||||||
|
|
||||||
if has_sufficient_direct_permission {
|
if has_sufficient_direct_permission {
|
||||||
// Check if user is WorkspaceAdmin or DataAdmin for this organization
|
// Compute the effective permission (highest of direct and workspace sharing)
|
||||||
let is_admin = user.organizations.iter().any(|org| {
|
let effective_permission = compute_effective_permission(
|
||||||
org.id == dashboard_file.organization_id
|
direct_permission_level,
|
||||||
&& (org.role == database::enums::UserOrganizationRole::WorkspaceAdmin
|
dashboard_file.workspace_sharing,
|
||||||
|| org.role == database::enums::UserOrganizationRole::DataAdmin)
|
dashboard_file.organization_id,
|
||||||
});
|
&user.organizations,
|
||||||
|
);
|
||||||
if is_admin {
|
|
||||||
// Admin users get Owner permissions
|
permission = effective_permission.unwrap_or(AssetPermissionRole::CanView);
|
||||||
permission = AssetPermissionRole::Owner;
|
tracing::debug!(
|
||||||
tracing::debug!(dashboard_id = %dashboard_id, user_id = %user.id, ?permission, "Granting Owner access to admin user.");
|
dashboard_id = %dashboard_id,
|
||||||
} else {
|
user_id = %user.id,
|
||||||
// User has direct permission, use that role
|
?direct_permission_level,
|
||||||
permission = direct_permission_level.unwrap_or(AssetPermissionRole::CanView); // Default just in case
|
workspace_sharing = ?dashboard_file.workspace_sharing,
|
||||||
tracing::debug!(dashboard_id = %dashboard_id, user_id = %user.id, ?permission, "Granting access via direct permission.");
|
?permission,
|
||||||
}
|
"Granting access with effective permission (max of direct and workspace sharing)."
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// No sufficient direct/admin permission, check if user has access via a chat
|
// 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.");
|
tracing::debug!(dashboard_id = %dashboard_id, "Insufficient direct/admin permission. Checking chat access.");
|
||||||
|
|
|
@ -17,7 +17,7 @@ use database::schema::{
|
||||||
asset_permissions, collections, collections_to_assets, dashboard_files, datasets, metric_files,
|
asset_permissions, collections, collections_to_assets, dashboard_files, datasets, metric_files,
|
||||||
metric_files_to_dashboard_files, metric_files_to_datasets, users,
|
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;
|
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");
|
tracing::debug!(metric_id = %metric_id, ?direct_permission_level, has_sufficient_direct_permission, "Direct permission check result");
|
||||||
|
|
||||||
if has_sufficient_direct_permission {
|
if has_sufficient_direct_permission {
|
||||||
// Check if user is WorkspaceAdmin or DataAdmin for this organization
|
// Compute the effective permission (highest of direct and workspace sharing)
|
||||||
let is_admin = user.organizations.iter().any(|org| {
|
let effective_permission = compute_effective_permission(
|
||||||
org.id == metric_file.organization_id
|
direct_permission_level,
|
||||||
&& (org.role == database::enums::UserOrganizationRole::WorkspaceAdmin
|
metric_file.workspace_sharing,
|
||||||
|| org.role == database::enums::UserOrganizationRole::DataAdmin)
|
metric_file.organization_id,
|
||||||
});
|
&user.organizations,
|
||||||
|
);
|
||||||
if is_admin {
|
|
||||||
// Admin users get Owner permissions
|
permission = effective_permission.unwrap_or(AssetPermissionRole::CanView);
|
||||||
permission = AssetPermissionRole::Owner;
|
tracing::debug!(
|
||||||
tracing::debug!(metric_id = %metric_id, user_id = %user.id, ?permission, "Granting Owner access to admin user.");
|
metric_id = %metric_id,
|
||||||
} else {
|
user_id = %user.id,
|
||||||
// User has direct permission, use that role
|
?direct_permission_level,
|
||||||
permission = direct_permission_level.unwrap_or(AssetPermissionRole::CanView); // Default just in case
|
workspace_sharing = ?metric_file.workspace_sharing,
|
||||||
tracing::debug!(metric_id = %metric_id, user_id = %user.id, ?permission, "Granting access via direct permission.");
|
?permission,
|
||||||
}
|
"Granting access with effective permission (max of direct and workspace sharing)."
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// No sufficient direct/admin permission, check if user has access via a dashboard
|
// 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.");
|
tracing::debug!(metric_id = %metric_id, "Insufficient direct/admin permission. Checking dashboard access.");
|
||||||
|
|
|
@ -15,7 +15,7 @@ use database::schema::{
|
||||||
asset_permissions, collections, collections_to_assets, dashboard_files, datasets,
|
asset_permissions, collections, collections_to_assets, dashboard_files, datasets,
|
||||||
metric_files_to_dashboard_files, users, metric_files_to_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;
|
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");
|
tracing::debug!(metric_id = %metric_id, ?direct_permission_level, has_sufficient_direct_permission, "Direct permission check result");
|
||||||
|
|
||||||
if has_sufficient_direct_permission {
|
if has_sufficient_direct_permission {
|
||||||
// Check if user is WorkspaceAdmin or DataAdmin for this organization
|
// Compute the effective permission (highest of direct and workspace sharing)
|
||||||
let is_admin = user.organizations.iter().any(|org| {
|
let effective_permission = compute_effective_permission(
|
||||||
org.id == metric_file.organization_id
|
direct_permission_level,
|
||||||
&& (org.role == database::enums::UserOrganizationRole::WorkspaceAdmin
|
metric_file.workspace_sharing,
|
||||||
|| org.role == database::enums::UserOrganizationRole::DataAdmin)
|
metric_file.organization_id,
|
||||||
});
|
&user.organizations,
|
||||||
|
);
|
||||||
if is_admin {
|
|
||||||
// Admin users get Owner permissions
|
permission = effective_permission.unwrap_or(AssetPermissionRole::CanView);
|
||||||
permission = AssetPermissionRole::Owner;
|
tracing::debug!(
|
||||||
tracing::debug!(metric_id = %metric_id, user_id = %user.id, ?permission, "Granting Owner access to admin user.");
|
metric_id = %metric_id,
|
||||||
} else {
|
user_id = %user.id,
|
||||||
// User has direct permission, use that role
|
?direct_permission_level,
|
||||||
permission = direct_permission_level.unwrap_or(AssetPermissionRole::CanView); // Default just in case
|
workspace_sharing = ?metric_file.workspace_sharing,
|
||||||
tracing::debug!(metric_id = %metric_id, user_id = %user.id, ?permission, "Granting access via direct permission.");
|
?permission,
|
||||||
}
|
"Granting access with effective permission (max of direct and workspace sharing)."
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// No sufficient direct/admin permission, check if user has access via a dashboard
|
// 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.");
|
tracing::debug!(metric_id = %metric_id, "Insufficient direct/admin permission. Checking dashboard access.");
|
||||||
|
|
|
@ -5,6 +5,63 @@ use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl, Opti
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use middleware::OrganizationMembership;
|
use middleware::OrganizationMembership;
|
||||||
use uuid::Uuid;
|
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.
|
/// Checks if a user has sufficient permissions based on organization roles and asset permissions.
|
||||||
///
|
///
|
||||||
|
|
|
@ -19,4 +19,8 @@ pub use types::{
|
||||||
SerializableAssetPermission, UserInfo,
|
SerializableAssetPermission, UserInfo,
|
||||||
};
|
};
|
||||||
pub use user_lookup::find_user_by_email;
|
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
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue