buster/api/prds/active/refactor_sharing_permission...

115 lines
6.0 KiB
Markdown
Raw Normal View History

2025-04-09 00:29:39 +08:00
# Sub-PRD: Simplified Asset Permission Helper
2025-04-08 23:20:20 +08:00
**Author:** AI Assistant (Pair Programming with User)
**Date:** 2023-10-27
**Status:** Proposed
**Parent PRD:** [Project: Granular Asset Permission Checks](mdc:prds/active/project_granular_asset_permissions.md)
## 1. Overview
2025-04-09 00:48:24 +08:00
This PRD details the creation of a simplified, centralized helper function within the `libs/sharing` crate. This function is primarily intended for contexts (like pre-execution checks where only an asset ID is available, e.g., `get_metric_data_handler` before running SQL) and an efficient permission check is needed. It leverages the user's cached organization roles and performs a targeted database query only for direct asset permissions. It differs from fetch helpers like `fetch_metric_file_with_permissions` which retrieve the full asset alongside its base permission.
2025-04-08 23:20:20 +08:00
## 2. Problem Statement
2025-04-09 00:48:24 +08:00
While handlers retrieving full asset metadata use efficient fetch helpers (like `fetch_..._with_permissions`), handlers performing actions based only on an asset ID (e.g., executing a query for a metric ID) need a way to check permissions without re-fetching the user's organization roles or the full asset. A dedicated helper using cached roles and querying only direct permissions is needed for these specific scenarios.
2025-04-08 23:20:20 +08:00
## 3. Goals
2025-04-09 00:29:39 +08:00
- Create a reusable, modular, and testable function `check_permission_for_asset_id` within `libs/sharing`.
- This function should accept user context (`AuthenticatedUser` containing cached org roles), asset details (ID, type, org ID), and required permission levels.
- It should first check the user's cached organization roles (`user.organizations`) for high-level admin privileges (`WorkspaceAdmin`, `DataAdmin`).
- If no admin role applies, it should query the `asset_permissions` table for direct permissions for the specific asset ID and user.
- It should return `Ok(true)` if the user meets the requirements (either via org admin role or direct permission), `Ok(false)` if they don't, and `Err` only for database query errors.
2025-04-08 23:20:20 +08:00
## 4. Non-Goals
2025-04-09 00:29:39 +08:00
- Replacing the efficient fetch helpers like `fetch_metric_file_with_permissions` used by metadata retrieval handlers.
- Querying the `users_to_organizations` table (relies on cached roles in `AuthenticatedUser`).
- Handling complex permission inheritance beyond direct permissions and high-level org roles (unless explicitly added).
2025-04-08 23:20:20 +08:00
## 5. Technical Design
2025-04-09 00:29:39 +08:00
1. **Location:** `libs/sharing/src/permissions.rs` (or similar).
2. **Function Signature & Implementation:**
2025-04-08 23:20:20 +08:00
```rust
// libs/sharing/src/permissions.rs
2025-04-09 00:29:39 +08:00
use anyhow::{anyhow, Result};
use diesel::prelude::*;
use diesel::dsl::exists;
2025-04-08 23:20:20 +08:00
use diesel_async::AsyncPgConnection;
use middleware::AuthenticatedUser;
2025-04-09 00:29:39 +08:00
use uuid::Uuid;
use database::enums::{AssetPermissionRole, AssetType, UserOrganizationRole, UserOrganizationStatus};
use database::schema::asset_permissions;
/// Checks if a user has the required permission level for a specific asset ID,
/// leveraging cached organization roles and querying direct permissions.
/// Intended primarily for pre-execution checks.
pub async fn check_permission_for_asset_id(
2025-04-08 23:20:20 +08:00
conn: &mut AsyncPgConnection,
user: &AuthenticatedUser,
asset_id: &Uuid,
asset_type: AssetType,
2025-04-09 00:29:39 +08:00
asset_organization_id: Uuid, // Org ID of the asset itself
2025-04-08 23:20:20 +08:00
required_roles: &[AssetPermissionRole],
) -> Result<bool> {
2025-04-09 00:29:39 +08:00
// --- 1. Check Cached High-Level Organization Roles First ---
let high_level_org_roles = [
2025-04-09 00:29:39 +08:00
UserOrganizationRole::WorkspaceAdmin,
UserOrganizationRole::DataAdmin,
];
2025-04-09 00:29:39 +08:00
let has_high_level_org_role = user.organizations.iter().any(|org| {
org.id == asset_organization_id && high_level_org_roles.contains(&org.role)
// Assuming AuthenticatedUser.organizations only contains active memberships
});
2025-04-09 00:29:39 +08:00
if has_high_level_org_role {
return Ok(true); // User is Org Admin/Data Admin for the asset's org, grant access
}
2025-04-09 00:29:39 +08:00
// --- 2. Check Direct Permissions (Database Query) ---
2025-04-08 23:20:20 +08:00
let direct_permission_exists = select(exists(
asset_permissions::table
.filter(asset_permissions::asset_id.eq(asset_id))
.filter(asset_permissions::asset_type.eq(asset_type))
2025-04-09 00:29:39 +08:00
.filter(asset_permissions::identity_id.eq(user.id))
2025-04-08 23:20:20 +08:00
.filter(asset_permissions::identity_type.eq(database::enums::IdentityType::User))
2025-04-09 00:29:39 +08:00
.filter(asset_permissions::deleted_at.is_null()) // Ignore deleted permissions
2025-04-08 23:20:20 +08:00
.filter(asset_permissions::role.eq_any(required_roles)),
))
.get_result::<bool>(conn)
.await;
match direct_permission_exists {
2025-04-09 00:29:39 +08:00
Ok(true) => Ok(true), // Found sufficient direct permission
Ok(false) => Ok(false), // No sufficient direct permission found
2025-04-08 23:20:20 +08:00
Err(e) => {
tracing::error!(
2025-04-09 00:29:39 +08:00
"DB error checking direct asset permissions for user {} asset {} type {:?}: {}",
user.id, asset_id, asset_type, e
2025-04-08 23:20:20 +08:00
);
2025-04-09 00:29:39 +08:00
Err(anyhow!("Failed to check direct asset permissions: {}", e))
2025-04-08 23:20:20 +08:00
}
}
2025-04-09 00:29:39 +08:00
// Note: No check for Member role here unless specifically required
// for action execution contexts, as metadata handlers use different logic.
2025-04-08 23:20:20 +08:00
}
```
3. **File Changes:**
- Create/Modify: `libs/sharing/src/permissions.rs`
2025-04-09 00:29:39 +08:00
- Modify: `libs/sharing/src/lib.rs` (to export the function)
2025-04-08 23:20:20 +08:00
## 6. Implementation Plan
2025-04-09 00:29:39 +08:00
1. Implement the `check_permission_for_asset_id` function as defined above.
2. Add comprehensive unit tests.
3. Export the function.
2025-04-08 23:20:20 +08:00
## 7. Testing Strategy
- **Unit Tests:**
2025-04-09 00:29:39 +08:00
- Mock `AuthenticatedUser` with various `organizations` states.
2025-04-09 00:48:24 +08:00
- Mock the `AsyncPgConnection` dependency (e.g., using `mockall` if the connection is passed via a trait, or by creating a mock connection object) to simulate different `asset_permissions` query results without hitting a real database.
2025-04-08 23:20:20 +08:00
- Test cases:
2025-04-09 00:48:24 +08:00
- User has `