mirror of https://github.com/buster-so/buster.git
12 KiB
12 KiB
title | author | date | status | parent_prd | ticket |
---|---|---|---|---|---|
Permission Field Fix | Claude | 2024-04-07 | Draft | project_bug_fixes_and_testing.md | BUS-1063 |
Permission Field Fix
Problem Statement
The permission field in asset responses is inconsistent and sometimes missing required information. This causes issues with permission checking and display in the UI. The current implementation has several issues:
Current behavior:
- Permission field sometimes missing from responses
- Inconsistent permission field format
- Missing role information
- Incorrect inherited permissions
- Lack of comprehensive testing for permission scenarios
Expected behavior:
- Consistent permission field presence
- Standardized permission format
- Complete role information
- Proper inheritance handling
- Comprehensive test coverage using new test infrastructure
Impact:
- User Impact: Incorrect permission display and access control
- System Impact: Inconsistent permission state
- Testing Impact: Missing edge cases and inheritance scenarios
Goals
- Fix permission field consistency
- Standardize permission format
- Add complete role information
- Fix permission inheritance
- Implement comprehensive tests using new test infrastructure
Non-Goals
- Adding new permission types
- Changing permission model
- Modifying permission UI
- Adding new permission features
Technical Design
Overview
The fix involves standardizing the permission field format and ensuring consistent inclusion in responses, with comprehensive testing using the new test infrastructure.
Permission Field Structure
// libs/models/src/permissions.rs
#[derive(Debug, Serialize, Deserialize)]
pub struct PermissionField {
pub role: AssetPermissionRole,
pub inherited: bool,
pub inherited_from: Option<Uuid>,
pub granted_at: DateTime<Utc>,
pub granted_by: Option<Uuid>,
}
impl PermissionField {
pub fn new(
role: AssetPermissionRole,
inherited: bool,
inherited_from: Option<Uuid>,
granted_by: Option<Uuid>,
) -> Self {
Self {
role,
inherited,
inherited_from,
granted_at: Utc::now(),
granted_by,
}
}
pub fn from_permission(permission: &Permission) -> Self {
Self {
role: permission.role,
inherited: permission.inherited,
inherited_from: permission.inherited_from,
granted_at: permission.granted_at,
granted_by: permission.granted_by,
}
}
}
Asset Response Update
// libs/models/src/asset.rs
#[derive(Debug, Serialize)]
pub struct AssetResponse {
pub id: Uuid,
pub name: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub permission: PermissionField, // Now required
// ... other fields ...
}
impl Asset {
pub async fn to_response(
&self,
user_id: Uuid,
conn: &mut PgConnection
) -> Result<AssetResponse> {
// Get user's permission
let permission = Permission::find_for_user(
self.id,
user_id,
conn
).await?;
Ok(AssetResponse {
id: self.id,
name: self.name.clone(),
created_at: self.created_at,
updated_at: self.updated_at,
permission: PermissionField::from_permission(&permission),
// ... other fields ...
})
}
}
Test Cases
// libs/models/tests/permission_field_test.rs
use database::tests::common::{TestDb, TestSetup};
use database::tests::common::permissions::PermissionTestHelpers;
use database::tests::common::assets::AssetTestHelpers;
#[tokio::test]
async fn test_permission_field_direct() -> Result<()> {
// Create test setup with admin user
let setup = TestSetup::new(Some(UserOrganizationRole::Admin)).await?;
// Create test asset using asset helpers
let asset_id = AssetTestHelpers::create_test_asset(
&setup.db,
"Test Asset"
).await?;
// Add direct permission using permission helpers
PermissionTestHelpers::create_permission(
&setup.db,
asset_id,
AssetPermissionRole::Owner
).await?;
// Get asset response using test db connection
let mut conn = setup.db.diesel_conn().await?;
let asset = Asset::find_by_id(asset_id).await?;
let response = asset.to_response(
setup.user.id,
&mut conn
).await?;
// Verify permission field
assert_eq!(response.permission.role, AssetPermissionRole::Owner);
assert!(!response.permission.inherited);
assert!(response.permission.inherited_from.is_none());
assert_eq!(
response.permission.granted_by,
Some(setup.user.id)
);
Ok(())
}
#[tokio::test]
async fn test_permission_field_inherited() -> Result<()> {
// Create test setup with admin user
let setup = TestSetup::new(Some(UserOrganizationRole::Admin)).await?;
// Create parent and child assets using asset helpers
let parent_id = AssetTestHelpers::create_test_asset(
&setup.db,
"Parent Asset"
).await?;
let child_id = AssetTestHelpers::create_test_asset(
&setup.db,
"Child Asset"
).await?;
// Add parent permission using permission helpers
PermissionTestHelpers::create_permission(
&setup.db,
parent_id,
AssetPermissionRole::Owner
).await?;
// Set inheritance using test db connection
let mut conn = setup.db.diesel_conn().await?;
diesel::update(assets::table)
.filter(assets::id.eq(child_id))
.set(assets::parent_id.eq(parent_id))
.execute(&mut conn)
.await?;
// Get child asset response
let child = Asset::find_by_id(child_id).await?;
let response = child.to_response(
setup.user.id,
&mut conn
).await?;
// Verify inherited permission field
assert_eq!(response.permission.role, AssetPermissionRole::Owner);
assert!(response.permission.inherited);
assert_eq!(response.permission.inherited_from, Some(parent_id));
Ok(())
}
#[tokio::test]
async fn test_permission_field_missing() -> Result<()> {
// Create test setup with viewer role
let setup = TestSetup::new(Some(UserOrganizationRole::Viewer)).await?;
// Create asset without permission using asset helpers
let asset_id = AssetTestHelpers::create_test_asset(
&setup.db,
"Test Asset"
).await?;
// Try to get asset response using test db connection
let mut conn = setup.db.diesel_conn().await?;
let asset = Asset::find_by_id(asset_id).await?;
let result = asset.to_response(
setup.user.id,
&mut conn
).await;
assert!(result.is_err());
// Verify error is logged using test db connection
let error_log = error_logs::table
.filter(error_logs::asset_id.eq(asset_id))
.first::<ErrorLog>(&mut conn)
.await?;
assert_eq!(error_log.error_type, "PermissionNotFound");
Ok(())
}
#[tokio::test]
async fn test_permission_field_edge_cases() -> Result<()> {
// Create test setup with admin user
let setup = TestSetup::new(Some(UserOrganizationRole::Admin)).await?;
// Create test assets using asset helpers
let asset_id = AssetTestHelpers::create_test_asset(
&setup.db,
"Test Asset"
).await?;
// Test edge cases
// Case 1: Multiple inheritance levels
let parent_id = AssetTestHelpers::create_test_asset(
&setup.db,
"Parent Asset"
).await?;
let grandparent_id = AssetTestHelpers::create_test_asset(
&setup.db,
"Grandparent Asset"
).await?;
// Set up inheritance chain
let mut conn = setup.db.diesel_conn().await?;
diesel::update(assets::table)
.filter(assets::id.eq(asset_id))
.set(assets::parent_id.eq(parent_id))
.execute(&mut conn)
.await?;
diesel::update(assets::table)
.filter(assets::id.eq(parent_id))
.set(assets::parent_id.eq(grandparent_id))
.execute(&mut conn)
.await?;
// Add permission to grandparent
PermissionTestHelpers::create_permission(
&setup.db,
grandparent_id,
AssetPermissionRole::Owner
).await?;
// Verify permission inheritance through chain
let asset = Asset::find_by_id(asset_id).await?;
let response = asset.to_response(
setup.user.id,
&mut conn
).await?;
assert_eq!(response.permission.role, AssetPermissionRole::Owner);
assert!(response.permission.inherited);
assert_eq!(response.permission.inherited_from, Some(grandparent_id));
// Case 2: Permission overrides
// Add direct permission that should override inherited
PermissionTestHelpers::create_permission(
&setup.db,
asset_id,
AssetPermissionRole::CanEdit
).await?;
let response = asset.to_response(
setup.user.id,
&mut conn
).await?;
// Direct permission should take precedence
assert_eq!(response.permission.role, AssetPermissionRole::CanEdit);
assert!(!response.permission.inherited);
assert!(response.permission.inherited_from.is_none());
// Case 3: Circular inheritance
// Create circular reference
diesel::update(assets::table)
.filter(assets::id.eq(grandparent_id))
.set(assets::parent_id.eq(asset_id))
.execute(&mut conn)
.await?;
// Should handle circular reference gracefully
let result = asset.to_response(
setup.user.id,
&mut conn
).await;
assert!(result.is_ok());
Ok(())
}
Dependencies
- Test infrastructure from Test Infrastructure Setup
- TestDb for database connections
- TestSetup for user/org creation
- PermissionTestHelpers for permission management
- AssetTestHelpers for asset creation
- Existing permission models
- Database schema
- Error logging system
Implementation Plan
Phase 1: Fix Implementation
- Update permission field structure
- Standardize response format
- Fix inheritance logic
- Add error handling
Phase 2: Testing
- Implement test cases using new test infrastructure:
- Direct permission tests
- Inheritance tests
- Missing permission tests
- Edge case tests
- Verify test isolation using TestDb
- Validate cleanup functionality
Testing Strategy
Unit Tests
-
Permission Field Tests
- Test field structure
- Verify serialization
- Check field validation
-
Permission Inheritance Tests
- Test direct permissions
- Test inherited permissions
- Test permission overrides
- Test inheritance chain
-
Edge Case Tests
- Test missing permissions
- Test circular inheritance
- Test multiple inheritance levels
- Test concurrent modifications
Integration Tests
-
API Tests
- Test permission field in responses
- Verify error handling
- Check inheritance behavior
-
Workflow Tests
- Test permission changes
- Verify inheritance updates
- Test with asset operations
Test Data Management
Using the new test infrastructure:
- Unique test IDs for isolation
- Automatic cleanup after tests
- Standardized test data creation
Success Criteria
- All tests passing using new infrastructure
- Permission fields consistent in responses
- Inheritance working correctly
- Edge cases handled properly
- Documentation updated
Security Considerations
-
Permission Validation
- Risk: Incorrect permission assignment
- Mitigation: Comprehensive permission tests
- Testing: Role-based access tests
-
Inheritance Security
- Risk: Unintended permission escalation
- Mitigation: Strict inheritance rules
- Testing: Inheritance chain validation
References
Appendix
Related Files
libs/models/src/permissions.rs
libs/models/src/asset.rs
libs/models/tests/permission_field_test.rs
libs/handlers/src/assets/*.rs
Permission Field Reference
Required fields:
- role: The user's role for the asset
- inherited: Whether the permission is inherited
- inherited_from: Parent asset ID if inherited
- granted_at: When the permission was granted
- granted_by: User ID who granted the permission